[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[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 = base.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 = base.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 = base.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, ActionUpdateProperty, 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, ActionUpdateProperty, 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, ActionUpdateProperty, 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, ActionUpdateProperty, 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         [EditorBrowsable(EditorBrowsableState.Never)]
532         protected int ActionJumpTo { get; set; } = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
533         #endregion Property
534
535
536         #region Method
537         /// <summary>
538         /// Set the minimum and the maximum frame.
539         /// </summary>
540         /// <param name="minFrame">minimum frame</param>
541         /// <param name="maxFrame">maximum frame</param>
542         /// <since_tizen> 7 </since_tizen>
543         public void SetMinMaxFrame(int minFrame, int maxFrame)
544         {
545             NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
546
547             currentStates.changed = true;
548             currentStates.framePlayRangeMin = minFrame;
549             currentStates.framePlayRangeMax = maxFrame;
550
551             PropertyArray array = new PropertyArray();
552             array.PushBack(new PropertyValue(currentStates.framePlayRangeMin));
553             array.PushBack(new PropertyValue(currentStates.framePlayRangeMax));
554
555             PropertyMap map = new PropertyMap();
556             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
557             DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, new PropertyValue(map));
558             NUILog.Debug($"  [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
559         }
560
561         /// <summary>
562         /// Play Animation.
563         /// </summary>
564         /// <since_tizen> 7 </since_tizen>
565         public new void Play()
566         {
567             NUILog.Debug($"<[{GetId()}] Play()");
568             debugPrint();
569             base.Play();
570             NUILog.Debug($"[{GetId()}]>");
571         }
572
573         /// <summary>
574         /// Pause Animation.
575         /// </summary>
576         /// <since_tizen> 7 </since_tizen>
577         public new void Pause()
578         {
579             NUILog.Debug($"<[{GetId()}] Pause()>");
580             debugPrint();
581             base.Pause();
582             NUILog.Debug($"[{GetId()}]>");
583         }
584
585         /// <summary>
586         /// Stop Animation.
587         /// </summary>
588         /// <since_tizen> 7 </since_tizen>
589         public new void Stop()
590         {
591             NUILog.Debug($"<[{GetId()}] Stop()");
592             debugPrint();
593             base.Stop();
594             NUILog.Debug($"[{GetId()}]>");
595         }
596
597         /// <summary>
598         /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
599         /// </summary>
600         /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
601         /// <since_tizen> 7 </since_tizen>
602         public List<Tuple<string, int, int>> GetContentInfo()
603         {
604             NUILog.Debug($"<");
605             if (currentStates.contentInfo != null)
606             {
607                 return currentStates.contentInfo;
608             }
609
610             PropertyMap imageMap = base.Image;
611             PropertyMap contentMap = new PropertyMap();
612             if (imageMap != null)
613             {
614                 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
615                 if (val != null)
616                 {
617                     if (val.Get(contentMap))
618                     {
619                         currentStates.contentInfo = new List<Tuple<string, int, int>>();
620                         for (uint i = 0; i < contentMap.Count(); i++)
621                         {
622                             string key = contentMap.GetKeyAt(i).StringKey;
623                             PropertyArray arr = new PropertyArray();
624                             contentMap.GetValue(i).Get(arr);
625                             if (arr != null)
626                             {
627                                 int startFrame, endFrame;
628                                 arr.GetElementAt(0).Get(out startFrame);
629                                 arr.GetElementAt(1).Get(out endFrame);
630
631                                 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
632
633                                 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
634
635                                 currentStates.contentInfo?.Add(item);
636                             }
637                         }
638                     }
639                 }
640             }
641             NUILog.Debug($">");
642             return currentStates.contentInfo;
643         }
644
645         /// <summary>
646         /// A marker has its start frame and end frame.
647         /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
648         /// 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.   *
649         /// </summary>
650         /// <param name="marker1">First marker</param>
651         /// <param name="marker2">Second marker</param>
652         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
653         [EditorBrowsable(EditorBrowsableState.Never)]
654         public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
655         {
656             NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
657
658             currentStates.changed = true;
659             currentStates.mark1 = marker1;
660             currentStates.mark2 = marker2;
661
662             PropertyArray array = new PropertyArray();
663             array.PushBack(new PropertyValue(currentStates.mark1));
664             if (marker2 != null)
665             {
666                 array.PushBack(new PropertyValue(currentStates.mark2));
667             }
668
669             PropertyMap map = new PropertyMap();
670             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
671             DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, new PropertyValue(map));
672             NUILog.Debug($"  [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
673         }
674
675         /// <summary>
676         /// Get MinMax Frame
677         /// </summary>
678         /// <returns>Tuple of Min and Max frames</returns>
679         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
680         [EditorBrowsable(EditorBrowsableState.Never)]
681         public Tuple<int, int> GetMinMaxFrame()
682         {
683             NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
684
685             PropertyMap map = Image;
686             if (map != null)
687             {
688                 PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
689                 if (val != null)
690                 {
691                     PropertyArray array = new PropertyArray();
692                     if (val.Get(array))
693                     {
694                         uint cnt = array.Count();
695                         int item1 = -1, item2 = -1;
696                         for (uint i = 0; i < cnt; i++)
697                         {
698                             PropertyValue v = array.GetElementAt(i);
699                             int intRet;
700                             if (v.Get(out intRet))
701                             {
702                                 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
703                                 if (i == 0)
704                                 {
705                                     item1 = intRet;
706                                 }
707                                 else if (i == 1)
708                                 {
709                                     item2 = intRet;
710                                 }
711                             }
712                             else
713                             {
714                                 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
715                             }
716                         }
717                         NUILog.Debug($"  [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
718                         return new Tuple<int, int>(item1, item2);
719                     }
720                 }
721             }
722             Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
723             return new Tuple<int, int>(-1, -1);
724         }
725         #endregion Method
726
727
728         #region Event, Enum, Struct, ETC
729         /// <summary>
730         /// Animation finished event.
731         /// </summary>
732         /// <since_tizen> 7 </since_tizen>
733         public event EventHandler Finished
734         {
735             add
736             {
737                 if (finishedEventHandler == null)
738                 {
739                     NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
740                     visualEventSignalCallback = onVisualEventSignal;
741                     VisualEventSignal().Connect(visualEventSignalCallback);
742                 }
743                 finishedEventHandler += value;
744             }
745             remove
746             {
747                 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
748                 finishedEventHandler -= value;
749                 if (finishedEventHandler == null && visualEventSignalCallback != null)
750                 {
751                     VisualEventSignal().Disconnect(visualEventSignalCallback);
752                 }
753             }
754         }
755
756         /// <summary>
757         /// Enumeration for what state the vector animation is in
758         /// </summary>
759         /// <since_tizen> 7 </since_tizen>
760         public enum PlayStateType
761         {
762             /// <summary>
763             /// Invalid
764             /// </summary>
765             /// <since_tizen> 7 </since_tizen>
766             Invalid = -1,
767             /// <summary>
768             /// Vector Animation has stopped
769             /// </summary>
770             /// <since_tizen> 7 </since_tizen>
771             Stopped = 0,
772             /// <summary>
773             /// The vector animation is playing
774             /// </summary>
775             /// <since_tizen> 7 </since_tizen>
776             Playing = 1,
777             /// <summary>
778             /// The vector animation is paused
779             /// </summary>
780             /// <since_tizen> 7 </since_tizen>
781             Paused = 2
782         }
783
784         /// <summary>
785         /// Enumeration for what to do when the animation is stopped.
786         /// </summary>
787         /// <since_tizen> 7 </since_tizen>
788         public enum StopBehaviorType
789         {
790             /// <summary>
791             /// When the animation is stopped, the current frame is shown.
792             /// </summary>
793             /// <since_tizen> 7 </since_tizen>
794             CurrentFrame,
795             /// <summary>
796             /// When the animation is stopped, the min frame (first frame) is shown.
797             /// </summary>
798             /// <since_tizen> 7 </since_tizen>
799             MinimumFrame,
800             /// <summary>
801             /// When the animation is stopped, the max frame (last frame) is shown.
802             /// </summary>
803             /// <since_tizen> 7 </since_tizen>
804             MaximumFrame
805         }
806
807         /// <summary>
808         /// Enumeration for what looping mode is in.
809         /// </summary>
810         /// <since_tizen> 7 </since_tizen>
811         public enum LoopingModeType
812         {
813             /// <summary>
814             /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
815             /// </summary>
816             /// <since_tizen> 7 </since_tizen>
817             Restart,
818             /// <summary>
819             /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
820             /// </summary>
821             /// <since_tizen> 7 </since_tizen>
822             AutoReverse
823         }
824         #endregion Event, Enum, Struct, ETC
825
826
827         #region Internal
828         internal class VisualEventSignalArgs : EventArgs
829         {
830             public int VisualIndex
831             {
832                 set;
833                 get;
834             }
835             public int SignalId
836             {
837                 set;
838                 get;
839             }
840         }
841
842         internal event EventHandler<VisualEventSignalArgs> VisualEvent
843         {
844             add
845             {
846                 if (visualEventSignalHandler == null)
847                 {
848                     visualEventSignalCallback = onVisualEventSignal;
849                     VisualEventSignal().Connect(visualEventSignalCallback);
850                 }
851                 visualEventSignalHandler += value;
852             }
853             remove
854             {
855                 visualEventSignalHandler -= value;
856                 if (visualEventSignalHandler == null && VisualEventSignal().Empty() == false)
857                 {
858                     VisualEventSignal().Disconnect(visualEventSignalCallback);
859                 }
860             }
861         }
862
863         internal void EmitVisualEventSignal(int visualIndex, int signalId)
864         {
865             VisualEventSignal().Emit(this, visualIndex, signalId);
866         }
867
868         internal VisualEventSignal VisualEventSignal()
869         {
870             VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
871             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
872             return ret;
873         }
874         #endregion Internal
875
876
877         #region Private
878         private struct states
879         {
880             internal string url;
881             internal int frame;
882             internal int loopCount;
883             internal LoopingModeType loopMode;
884             internal StopBehaviorType stopEndAction;
885             internal int framePlayRangeMin;
886             internal int framePlayRangeMax;
887             internal bool changed;
888             internal int totalFrame;
889             internal float scale;
890             internal PlayStateType playState;
891             internal List<Tuple<string, int, int>> contentInfo;
892             internal string mark1, mark2;
893             internal bool redrawInScalingDown;
894         };
895         private states currentStates;
896
897         private struct DevelVisual
898         {
899             internal enum Type
900             {
901                 AnimatedGradient = Visual.Type.AnimatedImage + 1,
902                 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
903             }
904         }
905
906         private const string tag = "NUITEST";
907         private event EventHandler finishedEventHandler;
908
909         private void OnFinished()
910         {
911             NUILog.Debug($"<[{GetId()}] OnFinished()>");
912             finishedEventHandler?.Invoke(this, null);
913         }
914
915         private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
916         {
917             OnFinished();
918
919             if (targetView != IntPtr.Zero)
920             {
921                 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
922                 if (v != null)
923                 {
924                     NUILog.Debug($"targetView is not null! name={v.Name}");
925                 }
926                 else
927                 {
928                     NUILog.Debug($"target is something created from dali");
929                 }
930             }
931             VisualEventSignalArgs e = new VisualEventSignalArgs();
932             e.VisualIndex = visualIndex;
933             e.SignalId = signalId;
934             visualEventSignalHandler?.Invoke(this, e);
935
936             NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
937         }
938
939         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
940         private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
941
942         private VisualEventSignalCallbackType visualEventSignalCallback;
943         private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
944
945         private void debugPrint()
946         {
947             NUILog.Debug($"===================================");
948             NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
949             NUILog.Debug($"  get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
950             NUILog.Debug($"  RedrawInScalingDown={RedrawInScalingDown} >");
951             NUILog.Debug($"===================================");
952         }
953         #endregion Private
954     }
955
956     /// <summary>
957     /// A class containing frame informations for a LottieAnimationView.
958     /// </summary>
959     [EditorBrowsable(EditorBrowsableState.Never)]
960     public class LottieFrameInfo : ICloneable
961     {
962         /// <summary>
963         /// Creates a new instance with a playing range.
964         /// </summary>
965         [EditorBrowsable(EditorBrowsableState.Never)]
966         public LottieFrameInfo(int startFrame, int endFrame)
967         {
968             StartFrame = startFrame;
969             EndFrame = endFrame;
970         }
971
972         /// <summary>
973         /// Creates a new instance with a still image frame.
974         /// </summary>
975         [EditorBrowsable(EditorBrowsableState.Never)]
976         public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
977         {
978         }
979
980         /// <summary>
981         /// Create a new instance from a pair notation.
982         /// </summary>
983         [EditorBrowsable(EditorBrowsableState.Never)]
984         public static implicit operator LottieFrameInfo((int, int) pair)
985         {
986             return new LottieFrameInfo(pair.Item1, pair.Item2);
987         }
988
989         /// <summary>
990         /// Create a new instance from an int value.
991         /// </summary>
992         [EditorBrowsable(EditorBrowsableState.Never)]
993         public static implicit operator LottieFrameInfo(int stillImageFrame)
994         {
995             return new LottieFrameInfo(stillImageFrame);
996         }
997
998         /// <summary>
999         /// Create a new instance from string.
1000         /// Possible input : "0, 10", "10"
1001         /// </summary>
1002         [EditorBrowsable(EditorBrowsableState.Never)]
1003         public static implicit operator LottieFrameInfo(string pair)
1004         {
1005             if (pair == null)
1006             {
1007                 return null;
1008             }
1009
1010             string[] parts = pair.Split(',');
1011             if (parts.Length == 1)
1012             {
1013                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1014             }
1015             else if (parts.Length == 2)
1016             {
1017                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1018             }
1019
1020             Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1021             return null;
1022         }
1023
1024         /// <summary>
1025         /// The start frame of the lottie animation.
1026         /// </summary>
1027         [EditorBrowsable(EditorBrowsableState.Never)]
1028         public int StartFrame { get; }
1029
1030         /// <summary>
1031         /// The end frame of the lottie animation.
1032         /// </summary>
1033         [EditorBrowsable(EditorBrowsableState.Never)]
1034         public int EndFrame { get; }
1035
1036         /// <summary>
1037         /// Create LottieFrameInfo struct with animation range information
1038         /// </summary>
1039         [EditorBrowsable(EditorBrowsableState.Never)]
1040         public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1041         {
1042             return new LottieFrameInfo(startFrame, endFrame);
1043         }
1044
1045         /// <summary>
1046         /// Create LottieFrameInfo struct with still image information
1047         /// </summary>
1048         [EditorBrowsable(EditorBrowsableState.Never)]
1049         public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1050         {
1051             return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1052         }
1053
1054         /// <summary>
1055         /// Inhouse API.
1056         /// Whether this LottieFrameInfo represents one frame or more.
1057         /// </summary>
1058         [EditorBrowsable(EditorBrowsableState.Never)]
1059         public bool IsStillImage()
1060         {
1061             return StartFrame == EndFrame;
1062         }
1063
1064         /// <summary>
1065         /// Inhouse API.
1066         /// Play specified LottieAnimationView with this frame information.
1067         /// </summary>
1068         /// <param name="lottieView">The target LottieAnimationView to play.</param>
1069         /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1070         [EditorBrowsable(EditorBrowsableState.Never)]
1071         public void Show(LottieAnimationView lottieView, bool noPlay = false)
1072         {
1073             if (!BeReadyToShow(lottieView))
1074             {
1075                 return;
1076             }
1077
1078             lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1079
1080             if (IsStillImage() || noPlay)
1081             {
1082                 lottieView.CurrentFrame = EndFrame;
1083             }
1084             else
1085             {
1086                 lottieView.CurrentFrame = StartFrame;
1087                 lottieView.Play();
1088             }
1089         }
1090
1091         /// <inheritdoc/>
1092         [EditorBrowsable(EditorBrowsableState.Never)]
1093         public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1094
1095         private bool BeReadyToShow(LottieAnimationView lottieView)
1096         {
1097             // Validate input lottieView
1098             if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1099             {
1100                 return false;
1101             }
1102
1103             // Stop if it was playing
1104             if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1105             {
1106                 lottieView.Stop();
1107             }
1108
1109             return true;
1110         }
1111     }
1112 }