bafde58af92d4119778b078ff24711cddcd2fcbc
[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.totalFrame = -1;
64             currentStates.scale = scale;
65             currentStates.redrawInScalingDown = true;
66
67             // Set changed flag as true when initalized state.
68             // After some properties change, LottieAnimationView.UpdateImage will apply these inital values.
69             currentStates.changed = true;
70             SetVisible(shown);
71         }
72
73         /// <summary>
74         /// Dispose(DisposeTypes type)
75         /// </summary>
76         /// <param name="type"></param>
77         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         protected override void Dispose(DisposeTypes type)
80         {
81             if (disposed)
82             {
83                 return;
84             }
85
86             CleanCallbackDictionaries();
87
88             //Release your own unmanaged resources here.
89             //You should not access any managed member here except static instance.
90             //because the execution order of Finalizes is non-deterministic.
91
92             //disconnect event signal
93             if (finishedEventHandler != null && visualEventSignalCallback != null)
94             {
95                 using VisualEventSignal visualEvent = VisualEventSignal();
96                 visualEvent.Disconnect(visualEventSignalCallback);
97                 finishedEventHandler = null;
98                 NUILog.Debug($"disconnect event signal");
99             }
100
101             base.Dispose(type);
102         }
103
104         // This is used for internal purpose. hidden API.
105         [EditorBrowsable(EditorBrowsableState.Never)]
106         protected override void Dispose(bool disposing)
107         {
108             CleanCallbackDictionaries();
109             base.Dispose(disposing);
110         }
111         #endregion Constructor, Destructor, Dispose
112
113
114         #region Property
115         /// <summary>
116         /// Set or Get resource URL of Lottie file.
117         /// </summary>
118         /// <since_tizen> 7 </since_tizen>
119         public string URL
120         {
121             get
122             {
123                 return GetValue(URLProperty) as string;
124             }
125             set
126             {
127                 SetValue(URLProperty, value);
128                 NotifyPropertyChanged();
129             }
130         }
131
132         private string InternalURL
133         {
134             set
135             {
136                 string ret = (value == null ? "" : value);
137                 currentStates.url = ret;
138
139                 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
140
141                 using PropertyMap map = new PropertyMap();
142                 using PropertyValue type = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
143                 using PropertyValue url = new PropertyValue(currentStates.url);
144                 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
145                 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
146                 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
147                 using PropertyValue redrawInScalingDown = new PropertyValue(currentStates.redrawInScalingDown);
148
149                 map.Add(Visual.Property.Type, type)
150                     .Add(ImageVisualProperty.URL, url)
151                     .Add(ImageVisualProperty.LoopCount, loopCnt)
152                     .Add(ImageVisualProperty.StopBehavior, stopAction)
153                     .Add(ImageVisualProperty.LoopingMode, loopMode)
154                     .Add(ImageVisualProperty.RedrawInScalingDown, redrawInScalingDown);
155                 Image = map;
156
157                 // All states applied well.
158                 currentStates.changed = false;
159
160                 currentStates.contentInfo = null;
161
162                 if (currentStates.scale != 1.0f)
163                 {
164                     Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
165                 }
166                 NUILog.Debug($"<[{GetId()}]>");
167             }
168             get
169             {
170                 string ret = currentStates.url;
171                 NUILog.Debug($"<[{GetId()}] GET");
172
173                 using PropertyMap map = base.Image;
174                 if (map != null)
175                 {
176                     using PropertyValue val = map.Find(ImageVisualProperty.URL);
177                     if (val != null)
178                     {
179                         if (val.Get(out ret))
180                         {
181                             NUILog.Debug($"gotten url={ret} >");
182                             return ret;
183                         }
184                     }
185                 }
186                 Tizen.Log.Error(tag, $"  [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
187                 return ret;
188             }
189         }
190
191         /// <summary>
192         /// Gets the playing state
193         /// </summary>
194         /// <since_tizen> 7 </since_tizen>
195         public PlayStateType PlayState
196         {
197             get
198             {
199                 NUILog.Debug($"< Get!");
200                 using PropertyMap map = base.Image;
201                 var ret = 0;
202                 if (map != null)
203                 {
204                     using PropertyValue val = map.Find(ImageVisualProperty.PlayState);
205                     if (val != null)
206                     {
207                         if (val.Get(out ret))
208                         {
209                             currentStates.playState = (PlayStateType)ret;
210                             NUILog.Debug($"gotten play state={ret} >");
211                         }
212                     }
213                 }
214                 else
215                 {
216                     Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
217                 }
218                 return currentStates.playState;
219             }
220         }
221
222         /// <summary>
223         /// Get the number of total frames
224         /// </summary>
225         /// <since_tizen> 7 </since_tizen>
226         public int TotalFrame
227         {
228             get
229             {
230                 int ret = -1;
231                 using PropertyMap map = base.Image;
232                 if (map != null)
233                 {
234                     using PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
235                     if (val != null)
236                     {
237                         if (val.Get(out ret))
238                         {
239                             NUILog.Debug($"TotalFrameNumber get! ret={ret}");
240                             currentStates.totalFrame = ret;
241                             return ret;
242                         }
243                     }
244                 }
245                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
246                 return ret;
247             }
248         }
249
250         /// <summary>
251         /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
252         /// </summary>
253         /// <remarks>
254         /// 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.
255         /// </remarks>
256         /// <example>
257         /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
258         /// <code>
259         /// LottieAnimationView myLottie = new LottieAnimationView();
260         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
261         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
262         /// myLottie.CurrentFrame = 200; //display 99 frame
263         /// myLottie.SetMinMaxFrame(10, 20);
264         /// myLottie.CurrentFrame = 15; //display 15 frame
265         /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
266         /// </code>
267         /// </example>
268         /// <since_tizen> 7 </since_tizen>
269         public int CurrentFrame
270         {
271             get
272             {
273                 return (int)GetValue(CurrentFrameProperty);
274             }
275             set
276             {
277                 SetValue(CurrentFrameProperty, value);
278                 NotifyPropertyChanged();
279             }
280         }
281
282         private int InternalCurrentFrame
283         {
284             set
285             {
286                 currentStates.frame = value;
287                 NUILog.Debug($"<[{GetId()}]SET frame={currentStates.frame}>");
288                 using PropertyValue attribute = new PropertyValue(currentStates.frame);
289                 DoAction(ImageView.Property.IMAGE, ActionJumpTo, attribute);
290             }
291             get
292             {
293                 int ret = 0;
294                 using PropertyMap map = base.Image;
295                 if (map != null)
296                 {
297                     using PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
298                     if (val != null)
299                     {
300                         if (val.Get(out ret))
301                         {
302                             NUILog.Debug($"CurrentFrameNumber get! val={ret}");
303                             return ret;
304                         }
305                     }
306                 }
307                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
308                 return ret;
309             }
310         }
311
312         /// <summary>
313         /// Sets or gets the looping mode of Lottie animation.
314         /// </summary>
315         /// <since_tizen> 7 </since_tizen>
316         public LoopingModeType LoopingMode
317         {
318             get
319             {
320                 return (LoopingModeType)GetValue(LoopingModeProperty);
321             }
322             set
323             {
324                 SetValue(LoopingModeProperty, value);
325                 NotifyPropertyChanged();
326             }
327         }
328
329         private LoopingModeType InternalLoopingMode
330         {
331             set
332             {
333                 currentStates.loopMode = (LoopingModeType)value;
334                 currentStates.changed = true;
335
336                 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
337                 using PropertyMap map = new PropertyMap();
338                 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
339                 map.Add(ImageVisualProperty.LoopingMode, loopMode);
340                 using PropertyValue attribute = new PropertyValue(map);
341                 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
342             }
343             get
344             {
345                 NUILog.Debug($"LoopMode get!");
346                 using PropertyMap map = base.Image;
347                 var ret = 0;
348                 if (map != null)
349                 {
350                     using PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
351                     if (val != null)
352                     {
353                         if (val.Get(out ret))
354                         {
355                             NUILog.Debug($"gotten LoopMode={ret}");
356                             if (ret != (int)currentStates.loopMode && ret > 0)
357                             {
358                                 NUILog.Debug($" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
359                             }
360                             currentStates.loopMode = (LoopingModeType)ret;
361                             return (LoopingModeType)ret;
362                         }
363                     }
364                 }
365                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
366                 return currentStates.loopMode;
367             }
368         }
369
370         /// <summary>
371         /// Sets or gets the loop count.
372         /// </summary>
373         /// <remarks>
374         /// The minus value means the infinite loop count.
375         /// </remarks>
376         /// <example>
377         /// <code>
378         /// LottieAnimationView myLottie = new LottieAnimationView();
379         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
380         /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
381         /// myLottie.LoopCount = -1; //infinite loop
382         /// myLottie.Play();
383         /// myLottie.Stop(); //it plays continuously unless Stop() is called
384         /// myLottie.LoopCount = 2;
385         /// myLottie.Play(); //it plays only 2 times and stops automatically
386         /// </code>
387         /// </example>
388         /// <since_tizen> 7 </since_tizen>
389         public int LoopCount
390         {
391             get
392             {
393                 return (int)GetValue(LoopCountProperty);
394             }
395             set
396             {
397                 SetValue(LoopCountProperty, value);
398                 NotifyPropertyChanged();
399             }
400         }
401
402         private int InternalLoopCount
403         {
404             set
405             {
406                 currentStates.changed = true;
407                 currentStates.loopCount = value;
408                 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
409                 using PropertyMap map = new PropertyMap();
410                 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
411                 map.Add(ImageVisualProperty.LoopCount, loopCnt);
412                 using PropertyValue attribute = new PropertyValue(map);
413                 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
414             }
415             get
416             {
417                 NUILog.Debug($"LoopCount get!");
418                 using PropertyMap map = base.Image;
419                 var ret = 0;
420                 if (map != null)
421                 {
422                     using PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
423                     if (val != null)
424                     {
425                         if (val.Get(out ret))
426                         {
427                             NUILog.Debug($"gotten loop count={ret}");
428                             if (ret != currentStates.loopCount && ret > 0)
429                             {
430                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
431                             }
432                             currentStates.loopCount = ret;
433                             return currentStates.loopCount;
434                         }
435                     }
436                 }
437                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali  currentStates.loopCount={currentStates.loopCount}>");
438                 return currentStates.loopCount;
439             }
440         }
441
442         /// <summary>
443         /// Sets or gets the stop behavior.
444         /// </summary>
445         /// <since_tizen> 7 </since_tizen>
446         public StopBehaviorType StopBehavior
447         {
448             get
449             {
450                 return (StopBehaviorType)GetValue(StopBehaviorProperty);
451             }
452             set
453             {
454                 SetValue(StopBehaviorProperty, value);
455                 NotifyPropertyChanged();
456             }
457         }
458
459         private StopBehaviorType InternalStopBehavior
460         {
461             set
462             {
463                 currentStates.stopEndAction = (StopBehaviorType)value;
464                 currentStates.changed = true;
465
466                 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
467                 using PropertyMap map = new PropertyMap();
468                 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
469                 map.Add(ImageVisualProperty.StopBehavior, stopAction);
470                 using PropertyValue attribute = new PropertyValue(map);
471                 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, attribute);
472             }
473             get
474             {
475                 NUILog.Debug($"StopBehavior get!");
476                 using PropertyMap map = base.Image;
477                 var ret = 0;
478                 if (map != null)
479                 {
480                     using PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
481                     if (val != null)
482                     {
483                         if (val.Get(out ret))
484                         {
485                             NUILog.Debug($"gotten StopBehavior={ret}");
486                             if (ret != (int)currentStates.stopEndAction)
487                             {
488                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
489                             }
490                             currentStates.stopEndAction = (StopBehaviorType)ret;
491                             return (StopBehaviorType)ret;
492                         }
493                     }
494                 }
495                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
496                 return currentStates.stopEndAction;
497             }
498         }
499
500         /// <summary>
501         /// Whether to redraw the image when the visual is scaled down.
502         /// </summary>
503         /// <remarks>
504         /// Inhouse API.
505         /// It is used in the AnimatedVectorImageVisual.The default is true.
506         /// </remarks>
507         [EditorBrowsable(EditorBrowsableState.Never)]
508         public bool RedrawInScalingDown
509         {
510             get
511             {
512                 return (bool)GetValue(RedrawInScalingDownProperty);
513             }
514             set
515             {
516                 SetValue(RedrawInScalingDownProperty, value);
517                 NotifyPropertyChanged();
518             }
519         }
520
521         private bool InternalRedrawInScalingDown
522         {
523             set
524             {
525                 currentStates.changed = true;
526                 currentStates.redrawInScalingDown = value;
527                 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
528                 using PropertyMap map = new PropertyMap();
529                 using PropertyValue redraw = new PropertyValue(currentStates.redrawInScalingDown);
530                 map.Add(ImageVisualProperty.RedrawInScalingDown, redraw);
531                 using PropertyValue action = new PropertyValue(map);
532                 DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
533             }
534             get
535             {
536                 PropertyMap map = base.Image;
537                 var ret = true;
538                 if (map != null)
539                 {
540                     using PropertyValue val = map.Find(ImageVisualProperty.RedrawInScalingDown);
541                     if (val != null)
542                     {
543                         if (val.Get(out ret))
544                         {
545                             if (ret != currentStates.redrawInScalingDown)
546                             {
547                                 NUILog.Debug($"<[ERROR][{GetId()}](LottieAnimationView) different redrawInScalingDown! gotten={ret}, redrawInScalingDown={currentStates.redrawInScalingDown}>");
548                             }
549                             currentStates.redrawInScalingDown = ret;
550                             return currentStates.redrawInScalingDown;
551                         }
552                     }
553                 }
554                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get redrawInScalingDown from dali currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
555                 return currentStates.redrawInScalingDown;
556             }
557         }
558
559
560         /// <summary>
561         /// Actions property value to Jump to the specified frame.
562         /// This property can be redefined by child class if it use different value.
563         /// </summary>
564         [EditorBrowsable(EditorBrowsableState.Never)]
565         protected int ActionJumpTo { get; set; } = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
566
567         // This is used for internal purpose. hidden API.
568         [EditorBrowsable(EditorBrowsableState.Never)]
569         protected int SetDynamicProperty => ActionJumpTo + 1;
570         #endregion Property
571
572
573         #region Method
574         /// <summary>
575         /// Set the minimum and the maximum frame.
576         /// </summary>
577         /// <param name="minFrame">minimum frame</param>
578         /// <param name="maxFrame">maximum frame</param>
579         /// <since_tizen> 7 </since_tizen>
580         public void SetMinMaxFrame(int minFrame, int maxFrame)
581         {
582             NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
583
584             currentStates.changed = true;
585             currentStates.framePlayRangeMin = minFrame;
586             currentStates.framePlayRangeMax = maxFrame;
587
588             using PropertyArray array = new PropertyArray();
589             using PropertyValue min = new PropertyValue(currentStates.framePlayRangeMin);
590             using PropertyValue max = new PropertyValue(currentStates.framePlayRangeMax);
591             array.PushBack(min);
592             array.PushBack(max);
593
594             using PropertyMap map = new PropertyMap();
595             using PropertyValue range = new PropertyValue(array);
596             map.Add(ImageVisualProperty.PlayRange, range);
597             using PropertyValue action = new PropertyValue(map);
598             DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, action);
599             NUILog.Debug($"  [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
600         }
601
602         /// <summary>
603         /// Play Animation.
604         /// </summary>
605         /// <since_tizen> 7 </since_tizen>
606         public new void Play()
607         {
608             NUILog.Debug($"<[{GetId()}] Play()");
609             debugPrint();
610             base.Play();
611             NUILog.Debug($"[{GetId()}]>");
612         }
613
614         /// <summary>
615         /// Pause Animation.
616         /// </summary>
617         /// <since_tizen> 7 </since_tizen>
618         public new void Pause()
619         {
620             NUILog.Debug($"<[{GetId()}] Pause()>");
621             debugPrint();
622             base.Pause();
623             NUILog.Debug($"[{GetId()}]>");
624         }
625
626         /// <summary>
627         /// Stop Animation.
628         /// </summary>
629         /// <since_tizen> 7 </since_tizen>
630         public new void Stop()
631         {
632             NUILog.Debug($"<[{GetId()}] Stop()");
633             debugPrint();
634             base.Stop();
635             NUILog.Debug($"[{GetId()}]>");
636         }
637
638         /// <summary>
639         /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
640         /// </summary>
641         /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
642         /// <since_tizen> 7 </since_tizen>
643         public List<Tuple<string, int, int>> GetContentInfo()
644         {
645             NUILog.Debug($"<");
646
647             if (currentStates.contentInfo != null)
648             {
649                 return currentStates.contentInfo;
650             }
651
652             PropertyMap imageMap = base.Image;
653             if (imageMap != null)
654             {
655                 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
656                 PropertyMap contentMap = new PropertyMap();
657                 if (val?.Get(ref contentMap) == true)
658                 {
659                     currentStates.contentInfo = new List<Tuple<string, int, int>>();
660                     for (uint i = 0; i < contentMap.Count(); i++)
661                     {
662                         using PropertyKey propertyKey = contentMap.GetKeyAt(i);
663                         string key = propertyKey.StringKey;
664
665                         using PropertyValue arrVal = contentMap.GetValue(i);
666                         using PropertyArray arr = new PropertyArray();
667                         if (arrVal.Get(arr))
668                         {
669                             int startFrame = -1;
670                             using PropertyValue start = arr.GetElementAt(0);
671                             start?.Get(out startFrame);
672
673                             int endFrame = -1;
674                             using PropertyValue end = arr.GetElementAt(1);
675                             end?.Get(out endFrame);
676
677                             NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
678
679                             Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
680
681                             currentStates.contentInfo?.Add(item);
682                         }
683                     }
684                 }
685                 contentMap.Dispose();
686                 val?.Dispose();
687             }
688             NUILog.Debug($">");
689
690             return currentStates.contentInfo;
691         }
692
693         /// <summary>
694         /// A marker has its start frame and end frame.
695         /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
696         /// 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.   *
697         /// </summary>
698         /// <param name="marker1">First marker</param>
699         /// <param name="marker2">Second marker</param>
700         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
701         [EditorBrowsable(EditorBrowsableState.Never)]
702         public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
703         {
704             NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
705
706             currentStates.changed = true;
707             currentStates.mark1 = marker1;
708             currentStates.mark2 = marker2;
709
710             using PropertyArray array = new PropertyArray();
711             using PropertyValue mark1 = new PropertyValue(currentStates.mark1);
712             array.PushBack(mark1);
713             using PropertyValue mark2 = new PropertyValue(currentStates.mark2);
714             if (marker2 != null)
715             {
716                 array.PushBack(mark2);
717             }
718
719             using PropertyMap map = new PropertyMap();
720             using PropertyValue range = new PropertyValue(array);
721             map.Add(ImageVisualProperty.PlayRange, range);
722             using PropertyValue actionProperty = new PropertyValue(map);
723             DoAction(ImageView.Property.IMAGE, ActionUpdateProperty, actionProperty);
724             NUILog.Debug($"  [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
725         }
726
727         /// <summary>
728         /// Get MinMax Frame
729         /// </summary>
730         /// <returns>Tuple of Min and Max frames</returns>
731         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
732         [EditorBrowsable(EditorBrowsableState.Never)]
733         public Tuple<int, int> GetMinMaxFrame()
734         {
735             NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
736
737             using PropertyMap map = Image;
738             if (map != null)
739             {
740                 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
741                 if (val != null)
742                 {
743                     using PropertyArray array = new PropertyArray();
744                     if (val.Get(array))
745                     {
746                         uint cnt = array.Count();
747                         int item1 = -1, item2 = -1;
748                         for (uint i = 0; i < cnt; i++)
749                         {
750                             using PropertyValue v = array.GetElementAt(i);
751                             int intRet;
752                             if (v.Get(out intRet))
753                             {
754                                 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
755                                 if (i == 0)
756                                 {
757                                     item1 = intRet;
758                                 }
759                                 else if (i == 1)
760                                 {
761                                     item2 = intRet;
762                                 }
763                             }
764                             else
765                             {
766                                 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
767                             }
768                         }
769                         NUILog.Debug($"  [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
770                         return new Tuple<int, int>(item1, item2);
771                     }
772                 }
773             }
774             Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
775             return new Tuple<int, int>(-1, -1);
776         }
777
778         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
779         [EditorBrowsable(EditorBrowsableState.Never)]
780         public void DoActionExtension(LottieAnimationViewDynamicProperty info)
781         {
782             dynamicPropertyCallbackId++;
783
784             weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
785             InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
786
787             Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, SetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
788
789             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
790         }
791
792         private void CleanCallbackDictionaries()
793         {
794             if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
795             {
796                 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
797                 {
798                     if (weakReferencesOfLottie.ContainsKey(key))
799                     {
800                         weakReferencesOfLottie.Remove(key);
801                     }
802                 }
803             }
804             InternalSavedDynamicPropertyCallbacks?.Clear();
805             InternalSavedDynamicPropertyCallbacks = null;
806         }
807
808         /// <summary>
809         /// Update lottie-image-relative properties synchronously.
810         /// After call this API, All image properties updated.
811         /// </summary>
812         [EditorBrowsable(EditorBrowsableState.Never)]
813         protected override void UpdateImage()
814         {
815             if (!imagePropertyUpdatedFlag) return;
816
817             // Update currentStates properties to cachedImagePropertyMap
818             if(currentStates.changed)
819             {
820                 UpdateImage(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
821                 UpdateImage(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
822                 UpdateImage(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
823                 UpdateImage(ImageVisualProperty.RedrawInScalingDown, new PropertyValue(currentStates.redrawInScalingDown));
824                 currentStates.changed = false;
825             }
826
827             using PropertyValue animatiedImage = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
828             UpdateImage(Visual.Property.Type, animatiedImage);
829
830             base.UpdateImage();
831         }
832
833         /// <summary>
834         /// Update NUI cached animated image visual property map by inputed property map.
835         /// And call base.MergeCachedImageVisualProperty()
836         /// </summary>
837         /// <remarks>
838         /// For performance issue, we will collect only "cachedLottieAnimationPropertyKeyList" hold in this class.
839         /// </remarks>
840         [EditorBrowsable(EditorBrowsableState.Never)]
841         protected override void MergeCachedImageVisualProperty(PropertyMap map)
842         {
843             if (map == null) return;
844             if (cachedImagePropertyMap == null)
845             {
846                 cachedImagePropertyMap = new PropertyMap();
847             }
848             foreach (var key in cachedLottieAnimationPropertyKeyList)
849             {
850                 PropertyValue value = map.Find(key);
851                 if (value != null)
852                 {
853                     // Update-or-Insert new value
854                     cachedImagePropertyMap[key] = value;
855                 }
856             }
857             base.MergeCachedImageVisualProperty(map);
858         }
859         #endregion Method
860
861
862         #region Event, Enum, Struct, ETC
863         /// <summary>
864         /// Animation finished event.
865         /// </summary>
866         /// <since_tizen> 7 </since_tizen>
867         public event EventHandler Finished
868         {
869             add
870             {
871                 if (finishedEventHandler == null)
872                 {
873                     NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
874                     visualEventSignalCallback = onVisualEventSignal;
875                     using VisualEventSignal visualEvent = VisualEventSignal();
876                     visualEvent.Connect(visualEventSignalCallback);
877                 }
878                 finishedEventHandler += value;
879             }
880             remove
881             {
882                 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
883                 finishedEventHandler -= value;
884                 if (finishedEventHandler == null && visualEventSignalCallback != null)
885                 {
886                     using VisualEventSignal visualEvent = VisualEventSignal();
887                     visualEvent.Disconnect(visualEventSignalCallback);
888                     if (visualEvent?.Empty() == true)
889                     {
890                         visualEventSignalCallback = null;
891                     }
892                 }
893             }
894         }
895
896         /// <summary>
897         /// Enumeration for what state the vector animation is in
898         /// </summary>
899         /// <since_tizen> 7 </since_tizen>
900         public enum PlayStateType
901         {
902             /// <summary>
903             /// Invalid
904             /// </summary>
905             /// <since_tizen> 7 </since_tizen>
906             Invalid = -1,
907             /// <summary>
908             /// Vector Animation has stopped
909             /// </summary>
910             /// <since_tizen> 7 </since_tizen>
911             Stopped = 0,
912             /// <summary>
913             /// The vector animation is playing
914             /// </summary>
915             /// <since_tizen> 7 </since_tizen>
916             Playing = 1,
917             /// <summary>
918             /// The vector animation is paused
919             /// </summary>
920             /// <since_tizen> 7 </since_tizen>
921             Paused = 2
922         }
923
924         /// <summary>
925         /// Enumeration for what to do when the animation is stopped.
926         /// </summary>
927         /// <since_tizen> 7 </since_tizen>
928         public enum StopBehaviorType
929         {
930             /// <summary>
931             /// When the animation is stopped, the current frame is shown.
932             /// </summary>
933             /// <since_tizen> 7 </since_tizen>
934             CurrentFrame,
935             /// <summary>
936             /// When the animation is stopped, the min frame (first frame) is shown.
937             /// </summary>
938             /// <since_tizen> 7 </since_tizen>
939             MinimumFrame,
940             /// <summary>
941             /// When the animation is stopped, the max frame (last frame) is shown.
942             /// </summary>
943             /// <since_tizen> 7 </since_tizen>
944             MaximumFrame
945         }
946
947         /// <summary>
948         /// Enumeration for what looping mode is in.
949         /// </summary>
950         /// <since_tizen> 7 </since_tizen>
951         public enum LoopingModeType
952         {
953             /// <summary>
954             /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
955             /// </summary>
956             /// <since_tizen> 7 </since_tizen>
957             Restart,
958             /// <summary>
959             /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
960             /// </summary>
961             /// <since_tizen> 7 </since_tizen>
962             AutoReverse
963         }
964
965         /// <summary>
966         /// Vector Property
967         /// </summary>
968         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
969         [EditorBrowsable(EditorBrowsableState.Never)]
970         public enum VectorProperty
971         {
972             /// <summary>
973             /// Fill color of the object, Type of <see cref="Vector3"/>
974             /// </summary>
975             [EditorBrowsable(EditorBrowsableState.Never)]
976             FillColor,
977
978             /// <summary>
979             /// Fill opacity of the object, Type of float
980             /// </summary>
981             [EditorBrowsable(EditorBrowsableState.Never)]
982             FillOpacity,
983
984             /// <summary>
985             /// Stroke color of the object, Type of <see cref="Vector3"/>
986             /// </summary>
987             [EditorBrowsable(EditorBrowsableState.Never)]
988             StrokeColor,
989
990             /// <summary>
991             /// Stroke opacity of the object, Type of float
992             /// </summary>
993             [EditorBrowsable(EditorBrowsableState.Never)]
994             StrokeOpacity,
995
996             /// <summary>
997             /// Stroke width of the object, Type of float
998             /// </summary>
999             [EditorBrowsable(EditorBrowsableState.Never)]
1000             StrokeWidth,
1001
1002             /// <summary>
1003             /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
1004             /// </summary>
1005             [EditorBrowsable(EditorBrowsableState.Never)]
1006             TransformAnchor,
1007
1008             /// <summary>
1009             /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
1010             /// </summary>
1011             [EditorBrowsable(EditorBrowsableState.Never)]
1012             TransformPosition,
1013
1014             /// <summary>
1015             /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
1016             /// </summary>
1017             [EditorBrowsable(EditorBrowsableState.Never)]
1018             TransformScale,
1019
1020             /// <summary>
1021             /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
1022             /// </summary>
1023             [EditorBrowsable(EditorBrowsableState.Never)]
1024             TransformRotation,
1025
1026             /// <summary>
1027             /// Transform opacity of the Layer and Group object, Type of float
1028             /// </summary>
1029             [EditorBrowsable(EditorBrowsableState.Never)]
1030             TransformOpacity
1031         };
1032
1033         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1034         [EditorBrowsable(EditorBrowsableState.Never)]
1035         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1036         public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
1037         #endregion Event, Enum, Struct, ETC
1038
1039
1040         #region Internal
1041         internal class VisualEventSignalArgs : EventArgs
1042         {
1043             public int VisualIndex
1044             {
1045                 set;
1046                 get;
1047             }
1048             public int SignalId
1049             {
1050                 set;
1051                 get;
1052             }
1053         }
1054
1055         internal event EventHandler<VisualEventSignalArgs> VisualEvent
1056         {
1057             add
1058             {
1059                 if (visualEventSignalHandler == null)
1060                 {
1061                     visualEventSignalCallback = onVisualEventSignal;
1062                     using VisualEventSignal visualEvent = VisualEventSignal();
1063                     visualEvent?.Connect(visualEventSignalCallback);
1064                 }
1065                 visualEventSignalHandler += value;
1066             }
1067             remove
1068             {
1069                 visualEventSignalHandler -= value;
1070                 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
1071                 {
1072                     using VisualEventSignal visualEvent = VisualEventSignal();
1073                     visualEvent?.Disconnect(visualEventSignalCallback);
1074                     if (visualEvent?.Empty() == true)
1075                     {
1076                         visualEventSignalCallback = null;
1077                     }
1078                 }
1079             }
1080         }
1081
1082         internal void EmitVisualEventSignal(int visualIndex, int signalId)
1083         {
1084             using VisualEventSignal visualEvent = VisualEventSignal();
1085             visualEvent?.Emit(this, visualIndex, signalId);
1086         }
1087
1088         internal VisualEventSignal VisualEventSignal()
1089         {
1090             VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
1091             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
1092             return ret;
1093         }
1094
1095         internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1096
1097         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1098         internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
1099
1100         internal RootCallbackType rootCallback = RootCallback;
1101
1102         static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
1103         {
1104             WeakReference<LottieAnimationView> current = null;
1105             LottieAnimationView currentView = null;
1106             DynamicPropertyCallbackType currentCallback = null;
1107             PropertyValue ret = null;
1108
1109             if (weakReferencesOfLottie.TryGetValue(id, out current))
1110             {
1111                 if (current.TryGetTarget(out currentView))
1112                 {
1113                     if (currentView != null && currentView.InternalSavedDynamicPropertyCallbacks != null &&
1114                         currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback))
1115                     {
1116                         ret = currentCallback?.Invoke(returnType, frameNumber);
1117                     }
1118                     else
1119                     {
1120                         Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1121                         return;
1122                     }
1123                 }
1124                 else
1125                 {
1126                     Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1127                     return;
1128                 }
1129             }
1130             else
1131             {
1132                 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1133                 return;
1134             }
1135
1136             switch (returnType)
1137             {
1138                 case (int)(VectorProperty.FillColor):
1139                 case (int)(VectorProperty.StrokeColor):
1140                     Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1141                     if ((ret != null) && ret.Get(tmpVector3))
1142                     {
1143                         val1 = tmpVector3.X;
1144                         val2 = tmpVector3.Y;
1145                         val3 = tmpVector3.Z;
1146                     }
1147                     tmpVector3.Dispose();
1148                     break;
1149
1150                 case (int)(VectorProperty.TransformAnchor):
1151                 case (int)(VectorProperty.TransformPosition):
1152                 case (int)(VectorProperty.TransformScale):
1153                     Vector2 tmpVector2 = new Vector2(-1, -1);
1154                     if ((ret != null) && ret.Get(tmpVector2))
1155                     {
1156                         val1 = tmpVector2.X;
1157                         val2 = tmpVector2.Y;
1158                     }
1159                     tmpVector2.Dispose();
1160                     break;
1161
1162                 case (int)(VectorProperty.FillOpacity):
1163                 case (int)(VectorProperty.StrokeOpacity):
1164                 case (int)(VectorProperty.StrokeWidth):
1165                 case (int)(VectorProperty.TransformRotation):
1166                 case (int)(VectorProperty.TransformOpacity):
1167                     float tmpFloat = -1;
1168                     if ((ret != null) && ret.Get(out tmpFloat))
1169                     {
1170                         val1 = tmpFloat;
1171                     }
1172                     break;
1173                 default:
1174                     //do nothing
1175                     break;
1176             }
1177             ret?.Dispose();
1178         }
1179         #endregion Internal
1180
1181
1182         #region Private
1183
1184         // Collection of lottie-image-sensitive properties.
1185         private static readonly List<int> cachedLottieAnimationPropertyKeyList = new List<int> {
1186             ImageVisualProperty.LoopCount,
1187             ImageVisualProperty.StopBehavior,
1188             ImageVisualProperty.LoopingMode,
1189             ImageVisualProperty.RedrawInScalingDown,
1190         };
1191
1192         private struct states
1193         {
1194             internal string url;
1195             internal int frame;
1196             internal int loopCount;
1197             internal LoopingModeType loopMode;
1198             internal StopBehaviorType stopEndAction;
1199             internal int framePlayRangeMin;
1200             internal int framePlayRangeMax;
1201             internal int totalFrame;
1202             internal float scale;
1203             internal PlayStateType playState;
1204             internal List<Tuple<string, int, int>> contentInfo;
1205             internal string mark1, mark2;
1206             internal bool redrawInScalingDown;
1207             internal bool changed;
1208         };
1209         private states currentStates;
1210
1211         private const string tag = "NUITEST";
1212         private event EventHandler finishedEventHandler;
1213
1214         private void OnFinished()
1215         {
1216             NUILog.Debug($"<[{GetId()}] OnFinished()>");
1217             finishedEventHandler?.Invoke(this, null);
1218         }
1219
1220         private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1221         {
1222             OnFinished();
1223
1224             if (targetView != IntPtr.Zero)
1225             {
1226                 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1227                 if (v != null)
1228                 {
1229                     NUILog.Debug($"targetView is not null! name={v.Name}");
1230                 }
1231                 else
1232                 {
1233                     NUILog.Debug($"target is something created from dali");
1234                 }
1235             }
1236             VisualEventSignalArgs e = new VisualEventSignalArgs();
1237             e.VisualIndex = visualIndex;
1238             e.SignalId = signalId;
1239             visualEventSignalHandler?.Invoke(this, e);
1240
1241             NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1242         }
1243
1244         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
1245         private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1246
1247         private VisualEventSignalCallbackType visualEventSignalCallback;
1248         private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1249
1250         static private int dynamicPropertyCallbackId = 0;
1251         //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1252         static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1253
1254         private void debugPrint()
1255         {
1256             NUILog.Debug($"===================================");
1257             NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1258             NUILog.Debug($"  get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1259             NUILog.Debug($"  RedrawInScalingDown={RedrawInScalingDown} >");
1260             NUILog.Debug($"===================================");
1261         }
1262
1263         #endregion Private
1264     }
1265
1266     /// <summary>
1267     /// A class containing frame informations for a LottieAnimationView.
1268     /// </summary>
1269     [EditorBrowsable(EditorBrowsableState.Never)]
1270     public class LottieFrameInfo : ICloneable
1271     {
1272         /// <summary>
1273         /// Creates a new instance with a playing range.
1274         /// </summary>
1275         [EditorBrowsable(EditorBrowsableState.Never)]
1276         public LottieFrameInfo(int startFrame, int endFrame)
1277         {
1278             StartFrame = startFrame;
1279             EndFrame = endFrame;
1280         }
1281
1282         /// <summary>
1283         /// Creates a new instance with a still image frame.
1284         /// </summary>
1285         [EditorBrowsable(EditorBrowsableState.Never)]
1286         public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1287         {
1288         }
1289
1290         /// <summary>
1291         /// Create a new instance from a pair notation.
1292         /// </summary>
1293         [EditorBrowsable(EditorBrowsableState.Never)]
1294         public static implicit operator LottieFrameInfo((int, int) pair)
1295         {
1296             return new LottieFrameInfo(pair.Item1, pair.Item2);
1297         }
1298
1299         /// <summary>
1300         /// Create a new instance from an int value.
1301         /// </summary>
1302         [EditorBrowsable(EditorBrowsableState.Never)]
1303         public static implicit operator LottieFrameInfo(int stillImageFrame)
1304         {
1305             return new LottieFrameInfo(stillImageFrame);
1306         }
1307
1308         /// <summary>
1309         /// Create a new instance from string.
1310         /// Possible input : "0, 10", "10"
1311         /// </summary>
1312         [EditorBrowsable(EditorBrowsableState.Never)]
1313         public static implicit operator LottieFrameInfo(string pair)
1314         {
1315             if (pair == null)
1316             {
1317                 return null;
1318             }
1319
1320             string[] parts = pair.Split(',');
1321             if (parts.Length == 1)
1322             {
1323                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1324             }
1325             else if (parts.Length == 2)
1326             {
1327                 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1328             }
1329
1330             Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1331             return null;
1332         }
1333
1334         /// <summary>
1335         /// The start frame of the lottie animation.
1336         /// </summary>
1337         [EditorBrowsable(EditorBrowsableState.Never)]
1338         public int StartFrame { get; }
1339
1340         /// <summary>
1341         /// The end frame of the lottie animation.
1342         /// </summary>
1343         [EditorBrowsable(EditorBrowsableState.Never)]
1344         public int EndFrame { get; }
1345
1346         /// <summary>
1347         /// Create LottieFrameInfo struct with animation range information
1348         /// </summary>
1349         [EditorBrowsable(EditorBrowsableState.Never)]
1350         public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1351         {
1352             return new LottieFrameInfo(startFrame, endFrame);
1353         }
1354
1355         /// <summary>
1356         /// Create LottieFrameInfo struct with still image information
1357         /// </summary>
1358         [EditorBrowsable(EditorBrowsableState.Never)]
1359         public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1360         {
1361             return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1362         }
1363
1364         /// <summary>
1365         /// Inhouse API.
1366         /// Whether this LottieFrameInfo represents one frame or more.
1367         /// </summary>
1368         [EditorBrowsable(EditorBrowsableState.Never)]
1369         public bool IsStillImage()
1370         {
1371             return StartFrame == EndFrame;
1372         }
1373
1374         /// <summary>
1375         /// Inhouse API.
1376         /// Play specified LottieAnimationView with this frame information.
1377         /// </summary>
1378         /// <param name="lottieView">The target LottieAnimationView to play.</param>
1379         /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1380         [EditorBrowsable(EditorBrowsableState.Never)]
1381         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1382         public void Show(LottieAnimationView lottieView, bool noPlay = false)
1383         {
1384             if (!BeReadyToShow(lottieView))
1385             {
1386                 return;
1387             }
1388
1389             lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1390
1391             if (IsStillImage() || noPlay)
1392             {
1393                 lottieView.CurrentFrame = EndFrame;
1394             }
1395             else
1396             {
1397                 lottieView.CurrentFrame = StartFrame;
1398                 lottieView.Play();
1399             }
1400         }
1401
1402         /// <inheritdoc/>
1403         [EditorBrowsable(EditorBrowsableState.Never)]
1404         public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1405
1406         private bool BeReadyToShow(LottieAnimationView lottieView)
1407         {
1408             // Validate input lottieView
1409             if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1410             {
1411                 return false;
1412             }
1413
1414             // Stop if it was playing
1415             if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1416             {
1417                 lottieView.Stop();
1418             }
1419
1420             return true;
1421         }
1422     }
1423
1424     // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1425     [EditorBrowsable(EditorBrowsableState.Never)]
1426     public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1427     {
1428         [EditorBrowsable(EditorBrowsableState.Never)]
1429         public string KeyPath { get; set; }
1430
1431         [EditorBrowsable(EditorBrowsableState.Never)]
1432         public LottieAnimationView.VectorProperty Property { get; set; }
1433
1434         [EditorBrowsable(EditorBrowsableState.Never)]
1435         public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1436
1437         public override bool Equals(object obj)
1438         {
1439             if (obj is LottieAnimationViewDynamicProperty target)
1440             {
1441                 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1442                 {
1443                     return true;
1444                 }
1445             }
1446             return false;
1447         }
1448
1449         public override int GetHashCode()
1450         {
1451             return base.GetHashCode();
1452         }
1453
1454         public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1455         {
1456             return left.Equals(right);
1457         }
1458
1459         public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1460         {
1461             return !(left == right);
1462         }
1463
1464         public bool Equals(LottieAnimationViewDynamicProperty other)
1465         {
1466             if (other != null)
1467             {
1468                 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)
1469                 {
1470                     return true;
1471                 }
1472             }
1473             return false;
1474         }
1475     }
1476
1477 }