2 * Copyright(c) 2019 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
24 using tlog = Tizen.Log;
27 namespace Tizen.NUI.BaseComponents
30 /// LottieAnimationView renders an animated vector image (Lottie file).
32 /// <since_tizen> 7 </since_tizen>
33 public class LottieAnimationView : ImageView
35 #region Constructor, Distructor, Dispose
37 /// LottieAnimationView constructor
39 /// <param name="scale">The factor of scaling image, default : 1.0f</param>
40 /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
42 /// If shown parameter is false, it is not visible even the LottieAnimationView instance is created.
46 /// LottieAnimationView myLottie = new LottieAnimationView();
47 /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
48 /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
51 /// <since_tizen> 7 </since_tizen>
52 public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
54 tlog.Fatal(tag, $"< constructor GetId={GetId()} >");
55 currentStates.url = "";
56 currentStates.frame = -1;
57 currentStates.loopCount = 1;
58 currentStates.loopMode = LoopingModeType.Restart;
59 currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
60 currentStates.framePlayRangeMin = -1;
61 currentStates.framePlayRangeMax = -1;
62 currentStates.changed = false;
63 currentStates.totalFrame = -1;
64 currentStates.scale = scale;
69 /// Dispose(DisposeTypes type)
71 /// <param name="type"></param>
72 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 protected override void Dispose(DisposeTypes type)
81 tlog.Fatal(tag, $"<[{GetId()}] type={type}");
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.
87 //disconnect event signal
88 if (finishedEventHandler != null && visualEventSignalCallback != null)
90 VisualEventSignal().Disconnect(visualEventSignalCallback);
91 finishedEventHandler = null;
92 tlog.Fatal(tag, $"disconnect event signal");
96 tlog.Fatal(tag, $"[{GetId()}]>");
98 #endregion Constructor, Distructor, Dispose
103 /// Set or Get resource URL of Lottie file.
105 /// <since_tizen> 7 </since_tizen>
110 string ret = (value == null ? "" : value);
111 currentStates.url = ret;
112 currentStates.changed = true;
114 tlog.Fatal(tag, $"<[{GetId()}]SET url={currentStates.url}");
116 PropertyMap map = new PropertyMap();
117 map.Add(Visual.Property.Type, new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage))
118 .Add(ImageVisualProperty.URL, new PropertyValue(currentStates.url))
119 .Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount))
120 .Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction))
121 .Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
124 currentStates.contentInfo = null;
126 if (currentStates.scale != 1.0f)
128 Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
130 tlog.Fatal(tag, $"<[{GetId()}]>");
134 string ret = currentStates.url;
135 tlog.Fatal(tag, $"<[{GetId()}] GET");
137 PropertyMap map = Image;
140 PropertyValue val = map.Find(ImageVisualProperty.URL);
143 if (val.Get(out ret))
145 tlog.Fatal(tag, $"gotten url={ret} >");
150 Tizen.Log.Error(tag, $" [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
156 /// Get playing state
158 /// <since_tizen> 7 </since_tizen>
159 public PlayStateType PlayState
163 tlog.Fatal(tag, $"< Get!");
164 PropertyMap map = base.Image;
168 PropertyValue val = map.Find(ImageVisualProperty.PlayState);
171 if (val.Get(out ret))
173 currentStates.playState = (PlayStateType)ret;
174 tlog.Fatal(tag, $"gotten play state={ret} >");
175 return currentStates.playState;
179 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
180 return currentStates.playState;
185 /// Get the number of total frame
187 /// <since_tizen> 7 </since_tizen>
188 public int TotalFrame
193 PropertyMap map = Image;
196 PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
199 if (val.Get(out ret))
201 //tlog.Fatal(tag, $"TotalFrameNumber get! ret={ret}");
202 currentStates.totalFrame = ret;
207 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
213 /// Set or Get current frame. When setting a specific frame, it is displayed as a still image.
216 /// Giving user set value when getting. If the out ranged frame is set, it is reset as minimum frame or maximum frame.
219 /// Let's say myLottie.json file has 100 frames originally, then it will have 0 ~ 99 range of frame index.
221 /// LottieAnimationView myLottie = new LottieAnimationView();
222 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
223 /// Window.Instance.GetDefaultLayer().Add(myLottie);
224 /// myLottie.CurrentFrame = 200; //display 99 frame
225 /// myLottie.SetMinMaxFrame(10, 20);
226 /// myLottie.CurrentFrame = 15; //display 15 frame
227 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
230 /// <since_tizen> 7 </since_tizen>
231 public int CurrentFrame
235 currentStates.frame = value;
236 tlog.Fatal(tag, $"<[{GetId()}]SET frame={currentStates.frame}>");
237 DoAction(vectorImageVisualIndex, (int)actionType.jumpTo, new PropertyValue(currentStates.frame));
242 PropertyMap map = Image;
245 PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
248 if (val.Get(out ret))
250 //tlog.Fatal(tag, $"CurrentFrameNumber get! val={ret}");
255 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
261 /// Set or Get looping mode of Lottie animation.
263 /// <since_tizen> 7 </since_tizen>
264 public LoopingModeType LoopingMode
268 currentStates.loopMode = (LoopingModeType)value;
269 currentStates.changed = true;
271 tlog.Fatal(tag, $"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
272 PropertyMap map = new PropertyMap();
273 map.Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
274 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
278 //tlog.Fatal(tag, $"LoopMode get!");
279 PropertyMap map = base.Image;
283 PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
286 if (val.Get(out ret))
288 //tlog.Fatal(tag, $"gotten LoopMode={ret}");
289 if (ret != (int)currentStates.loopMode && ret > 0)
291 tlog.Fatal(tag, $" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
293 currentStates.loopMode = (LoopingModeType)ret;
294 return (LoopingModeType)ret;
298 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
299 return currentStates.loopMode;
304 /// Set or Get loop count.
307 /// Minus value means infinite loop count.
311 /// LottieAnimationView myLottie = new LottieAnimationView();
312 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
313 /// Window.Instance.GetDefaultLayer().Add(myLottie);
314 /// myLottie.LoopCount = -1; //infinite loop
316 /// myLottie.Stop(); //it plays continuously unless Stop() is called
317 /// myLottie.LoopCount = 2;
318 /// myLottie.Play(); //it plays only 2 times and stops automatically
321 /// <since_tizen> 7 </since_tizen>
326 currentStates.changed = true;
327 currentStates.loopCount = value;
328 tlog.Fatal(tag, $"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
329 PropertyMap map = new PropertyMap();
330 map.Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
331 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
335 //tlog.Fatal(tag, $"LoopCount get!");
336 PropertyMap map = base.Image;
340 PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
343 if (val.Get(out ret))
345 //tlog.Fatal(tag, $"gotten loop count={ret}");
346 if (ret != currentStates.loopCount && ret > 0)
348 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
350 currentStates.loopCount = ret;
351 return currentStates.loopCount;
355 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali currentStates.loopCount={currentStates.loopCount}>");
356 return currentStates.loopCount;
361 /// Set or Get stop behavior.
363 /// <since_tizen> 7 </since_tizen>
364 public StopBehaviorType StopBehavior
368 currentStates.stopEndAction = (StopBehaviorType)value;
369 currentStates.changed = true;
371 tlog.Fatal(tag, $"<[{GetId()}]SET val={currentStates.stopEndAction}>");
372 PropertyMap map = new PropertyMap();
373 map.Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
374 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
378 //tlog.Fatal(tag, $"StopBehavior get!");
379 PropertyMap map = base.Image;
383 PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
386 if (val.Get(out ret))
388 //tlog.Fatal(tag, $"gotten StopBehavior={ret}");
389 if (ret != (int)currentStates.stopEndAction)
391 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
393 currentStates.stopEndAction = (StopBehaviorType)ret;
394 return (StopBehaviorType)ret;
398 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
399 return currentStates.stopEndAction;
407 /// Set minimum and maximum frame.
409 /// <param name="minFrame">minimum frame</param>
410 /// <param name="maxFrame">maximum frame</param>
411 /// <since_tizen> 7 </since_tizen>
412 public void SetMinMaxFrame(int minFrame, int maxFrame)
414 tlog.Fatal(tag, $"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
416 currentStates.changed = true;
417 currentStates.framePlayRangeMin = minFrame;
418 currentStates.framePlayRangeMax = maxFrame;
420 PropertyArray array = new PropertyArray();
421 array.PushBack(new PropertyValue(currentStates.framePlayRangeMin));
422 array.PushBack(new PropertyValue(currentStates.framePlayRangeMax));
424 PropertyMap map = new PropertyMap();
425 map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
426 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
427 tlog.Fatal(tag, $" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
433 /// <since_tizen> 7 </since_tizen>
434 public new void Play()
436 tlog.Fatal(tag, $"<[{GetId()}] Play()");
439 tlog.Fatal(tag, $"[{GetId()}]>");
445 /// <since_tizen> 7 </since_tizen>
446 public new void Pause()
448 tlog.Fatal(tag, $"<[{GetId()}] Pause()>");
451 tlog.Fatal(tag, $"[{GetId()}]>");
457 /// <since_tizen> 7 </since_tizen>
458 public new void Stop()
460 tlog.Fatal(tag, $"<[{GetId()}] Stop()");
463 tlog.Fatal(tag, $"[{GetId()}]>");
467 /// Get the list of layers' information (start frame, end frame) in Lottie file.
469 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
470 /// <since_tizen> 7 </since_tizen>
471 public List<Tuple<string, int, int>> GetContentInfo()
473 tlog.Fatal(tag, $"<");
474 if (currentStates.contentInfo != null)
476 return currentStates.contentInfo;
479 PropertyMap imageMap = base.Image;
480 PropertyMap contentMap = new PropertyMap();
481 if (imageMap != null)
483 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
486 if (val.Get(contentMap))
488 for (uint i = 0; i < contentMap.Count(); i++)
490 string key = contentMap.GetKeyAt(i).StringKey;
491 PropertyArray arr = new PropertyArray();
492 contentMap.GetValue(i).Get(arr);
495 int startFrame, endFrame;
496 arr.GetElementAt(0).Get(out startFrame);
497 arr.GetElementAt(1).Get(out endFrame);
499 tlog.Fatal(tag, $"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
501 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
503 currentStates.contentInfo?.Add(item);
509 tlog.Fatal(tag, $">");
510 return currentStates.contentInfo;
514 /// A marker has its start frame and end frame.
515 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
516 /// 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. *
518 /// <param name="marker1">First marker</param>
519 /// <param name="marker2">Second marker</param>
520 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
521 [EditorBrowsable(EditorBrowsableState.Never)]
522 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
524 tlog.Fatal(tag, $"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
526 currentStates.changed = true;
527 currentStates.mark1 = marker1;
528 currentStates.mark2 = marker2;
530 PropertyArray array = new PropertyArray();
531 array.PushBack(new PropertyValue(currentStates.mark1));
534 array.PushBack(new PropertyValue(currentStates.mark2));
537 PropertyMap map = new PropertyMap();
538 map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
539 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
540 tlog.Fatal(tag, $" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
545 #region Event, Enum, Struct, ETC
547 /// Animation finished event.
549 /// <since_tizen> 7 </since_tizen>
550 public event EventHandler Finished
554 if (finishedEventHandler == null)
556 tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler added>");
557 visualEventSignalCallback = onVisualEventSignal;
558 VisualEventSignal().Connect(visualEventSignalCallback);
560 finishedEventHandler += value;
564 tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler removed>");
565 finishedEventHandler -= value;
566 if (finishedEventHandler == null && visualEventSignalCallback != null)
568 VisualEventSignal().Disconnect(visualEventSignalCallback);
574 /// Enumeration for what state the vector animation is in
576 /// <since_tizen> 7 </since_tizen>
577 public enum PlayStateType
582 /// <since_tizen> 7 </since_tizen>
585 /// Vector Animation has stopped
587 /// <since_tizen> 7 </since_tizen>
590 /// The vector animation is playing
592 /// <since_tizen> 7 </since_tizen>
595 /// The vector animation is paused
597 /// <since_tizen> 7 </since_tizen>
602 /// Enumeration for what to do when the animation is stopped.
604 /// <since_tizen> 7 </since_tizen>
605 public enum StopBehaviorType
608 /// When the animation is stopped, the current frame is shown.
610 /// <since_tizen> 7 </since_tizen>
613 /// When the animation is stopped, the min frame (first frame) is shown.
615 /// <since_tizen> 7 </since_tizen>
618 /// When the animation is stopped, the max frame (last frame) is shown.
620 /// <since_tizen> 7 </since_tizen>
625 /// Enumeration for what looping mode is in.
627 /// <since_tizen> 7 </since_tizen>
628 public enum LoopingModeType
631 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
633 /// <since_tizen> 7 </since_tizen>
636 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
638 /// <since_tizen> 7 </since_tizen>
641 #endregion Event, Enum, Struct, ETC
645 internal class VisualEventSignalArgs : EventArgs
647 public int VisualIndex
659 internal event EventHandler<VisualEventSignalArgs> VisualEvent
663 if (visualEventSignalHandler == null)
665 visualEventSignalCallback = onVisualEventSignal;
666 VisualEventSignal().Connect(visualEventSignalCallback);
668 visualEventSignalHandler += value;
672 visualEventSignalHandler -= value;
673 if (visualEventSignalHandler == null && VisualEventSignal().Empty() == false)
675 VisualEventSignal().Disconnect(visualEventSignalCallback);
680 internal void EmitVisualEventSignal(int visualIndex, int signalId)
682 VisualEventSignal().Emit(this, visualIndex, signalId);
685 internal VisualEventSignal VisualEventSignal()
687 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
688 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
695 private struct states
699 internal int loopCount;
700 internal LoopingModeType loopMode;
701 internal StopBehaviorType stopEndAction;
702 internal int framePlayRangeMin;
703 internal int framePlayRangeMax;
704 internal bool changed;
705 internal int totalFrame;
706 internal float scale;
707 internal PlayStateType playState;
708 internal List<Tuple<string, int, int>> contentInfo;
709 internal string mark1, mark2;
711 private states currentStates;
713 private enum actionType
722 private struct DevelVisual
726 AnimatedGradient = Visual.Type.AnimatedImage + 1,
727 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
731 private const string tag = "NUITEST";
732 private const int vectorImageVisualIndex = 10000000 + 1000 + 2;
733 private event EventHandler finishedEventHandler;
735 private void OnFinished()
737 tlog.Fatal(tag, $"<[{GetId()}] OnFinished()>");
738 finishedEventHandler?.Invoke(this, null);
741 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
745 if (targetView != IntPtr.Zero)
747 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
750 tlog.Fatal(tag, $"targetView is not null! name={v.Name}");
754 tlog.Fatal(tag, $"target is something created from dali");
757 VisualEventSignalArgs e = new VisualEventSignalArgs();
758 e.VisualIndex = visualIndex;
759 e.SignalId = signalId;
760 visualEventSignalHandler?.Invoke(this, e);
762 tlog.Fatal(tag, $"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
765 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
766 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
768 private VisualEventSignalCallbackType visualEventSignalCallback;
769 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
771 private void debugPrint()
773 tlog.Fatal(tag, $"===================================");
774 tlog.Fatal(tag, $"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, framePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
775 tlog.Fatal(tag, $" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState} >");
776 tlog.Fatal(tag, $"===================================");