diff --git a/Lottie.Android.net6/Additions/AboutAdditions.txt b/Lottie.Android.net6/Additions/AboutAdditions.txt
new file mode 100644
index 0000000..c511f1d
--- /dev/null
+++ b/Lottie.Android.net6/Additions/AboutAdditions.txt
@@ -0,0 +1,48 @@
+Additions allow you to add arbitrary C# to the generated classes
+before they are compiled. This can be helpful for providing convenience
+methods or adding pure C# classes.
+
+== Adding Methods to Generated Classes ==
+
+Let's say the library being bound has a Rectangle class with a constructor
+that takes an x and y position, and a width and length size. It will look like
+this:
+
+public partial class Rectangle
+{
+ public Rectangle (int x, int y, int width, int height)
+ {
+ // JNI bindings
+ }
+}
+
+Imagine we want to add a constructor to this class that takes a Point and
+Size structure instead of 4 ints. We can add a new file called Rectangle.cs
+with a partial class containing our new method:
+
+public partial class Rectangle
+{
+ public Rectangle (Point location, Size size) :
+ this (location.X, location.Y, size.Width, size.Height)
+ {
+ }
+}
+
+At compile time, the additions class will be added to the generated class
+and the final assembly will a Rectangle class with both constructors.
+
+
+== Adding C# Classes ==
+
+Another thing that can be done is adding fully C# managed classes to the
+generated library. In the above example, let's assume that there isn't a
+Point class available in Java or our library. The one we create doesn't need
+to interact with Java, so we'll create it like a normal class in C#.
+
+By adding a Point.cs file with this class, it will end up in the binding library:
+
+public class Point
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+}
diff --git a/Lottie.Android.net6/Additions/LottieAnimationView.cs b/Lottie.Android.net6/Additions/LottieAnimationView.cs
new file mode 100644
index 0000000..db02201
--- /dev/null
+++ b/Lottie.Android.net6/Additions/LottieAnimationView.cs
@@ -0,0 +1,31 @@
+using System;
+using Android.Graphics;
+
+namespace Com.Airbnb.Lottie
+{
+ public partial class LottieAnimationView
+ {
+ ///
+ /// Delegate to handle the loading of bitmaps that are not packaged in the assets of your app.
+ ///
+ public void SetImageAssetDelegate(Func funcAssetLoad)
+ {
+ this.SetImageAssetDelegate(new ImageAssetDelegateImpl(funcAssetLoad));
+ }
+
+ internal sealed class ImageAssetDelegateImpl : Java.Lang.Object, IImageAssetDelegate
+ {
+ private readonly Func funcAssetLoad;
+
+ public ImageAssetDelegateImpl(Func funcAssetLoad)
+ {
+ this.funcAssetLoad = funcAssetLoad;
+ }
+
+ public Bitmap FetchBitmap(LottieImageAsset asset)
+ {
+ return this.funcAssetLoad(asset);
+ }
+ }
+ }
+}
diff --git a/Lottie.Android.net6/Additions/LottieComposition.cs b/Lottie.Android.net6/Additions/LottieComposition.cs
new file mode 100644
index 0000000..f18ba3d
--- /dev/null
+++ b/Lottie.Android.net6/Additions/LottieComposition.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Android.Content;
+
+namespace Com.Airbnb.Lottie
+{
+ public partial class LottieComposition
+ {
+ public partial class Factory
+ {
+ ///
+ /// Asynchronously loads a composition from a file stored in /assets.
+ ///
+ public static ICancellable FromAssetFileName(Context context, string fileName, Action onLoaded)
+ {
+ return Factory.FromAssetFileName(context, fileName, new ActionCompositionLoaded(onLoaded));
+ }
+
+ ///
+ /// Asynchronously loads a composition from an arbitrary input stream.
+ ///
+ public static ICancellable FromInputStream(System.IO.Stream stream, Action onLoaded)
+ {
+ return Factory.FromInputStream(stream, new ActionCompositionLoaded(onLoaded));
+ }
+
+ ///
+ /// Asynchronously loads a composition from a json string. This is useful for animations loaded from the network.
+ ///
+ public static ICancellable FromJsonString(string jsonString, Action onLoaded)
+ {
+ return Factory.FromJsonString(jsonString, new ActionCompositionLoaded(onLoaded));
+ }
+
+ /////
+ ///// Asynchronously loads a composition from a file stored in /assets.
+ /////
+ public static Task FromAssetFileNameAsync(Context context, string fileName)
+ {
+ return FromAssetFileNameAsync(context, fileName, CancellationToken.None);
+ }
+
+ ///////
+ /////// Asynchronously loads a composition from a file stored in /assets.
+ ///////
+ public static Task FromAssetFileNameAsync(Context context, string fileName, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ var tcs = new TaskCompletionSource();
+ var cancelable = Factory.FromAssetFileName(context, fileName, (composition) =>
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ tcs.SetResult(composition);
+ });
+
+ cancellationToken.Register(() =>
+ {
+ if (!tcs.Task.IsCompleted)
+ {
+ cancelable.Cancel();
+ tcs.TrySetCanceled(cancellationToken);
+ }
+ });
+
+ return tcs.Task;
+ }
+
+ /////
+ ///// Asynchronously loads a composition from an arbitrary input stream.
+ /////
+ public static Task FromInputStreamAsync(Context context, System.IO.Stream stream)
+ {
+ return FromInputStreamAsync(stream, CancellationToken.None);
+ }
+
+ /////
+ ///// Asynchronously loads a composition from an arbitrary input stream.
+ /////
+ public static Task FromInputStreamAsync(System.IO.Stream stream, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ var tcs = new TaskCompletionSource();
+ var cancelable = Factory.FromInputStream(stream, (composition) =>
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ tcs.SetResult(composition);
+ });
+
+ cancellationToken.Register(() =>
+ {
+ if (!tcs.Task.IsCompleted)
+ {
+ cancelable.Cancel();
+ tcs.TrySetCanceled(cancellationToken);
+ }
+ });
+
+ return tcs.Task;
+ }
+
+ /////
+ ///// Asynchronously loads a composition from a raw json object. This is useful for animations loaded from the network.
+ /////
+ public static Task FromJsonStringAsync(string jsonString)
+ {
+ return FromJsonStringAsync(jsonString, CancellationToken.None);
+ }
+
+ /////
+ ///// Asynchronously loads a composition from a raw json object. This is useful for animations loaded from the network.
+ /////
+ public static Task FromJsonStringAsync(string jsonString, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ var tcs = new TaskCompletionSource();
+ var cancelable = Factory.FromJsonString(jsonString, (composition) =>
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ tcs.SetResult(composition);
+ });
+
+ cancellationToken.Register(() =>
+ {
+ if (!tcs.Task.IsCompleted)
+ {
+ cancelable.Cancel();
+ tcs.TrySetCanceled(cancellationToken);
+ }
+ });
+
+ return tcs.Task;
+ }
+
+ internal sealed class ActionCompositionLoaded : Java.Lang.Object, IOnCompositionLoadedListener
+ {
+ private readonly Action onLoaded;
+
+ public ActionCompositionLoaded(Action onLoaded)
+ {
+ this.onLoaded = onLoaded;
+ }
+
+ public void OnCompositionLoaded(LottieComposition compostion)
+ {
+ if (onLoaded != null)
+ {
+ onLoaded(compostion);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lottie.Android.net6/Jars/AboutJars.txt b/Lottie.Android.net6/Jars/AboutJars.txt
new file mode 100644
index 0000000..e833d78
--- /dev/null
+++ b/Lottie.Android.net6/Jars/AboutJars.txt
@@ -0,0 +1,36 @@
+This directory is for Android .jars.
+
+There are 4 types of jars that are supported:
+
+== Input Jar and Embedded Jar ==
+
+This is the jar that bindings should be generated for.
+
+For example, if you were binding the Google Maps library, this would
+be Google's "maps.jar".
+
+The difference between EmbeddedJar and InputJar is, EmbeddedJar is to be
+embedded in the resulting dll as EmbeddedResource, while InputJar is not.
+There are couple of reasons you wouldn't like to embed the target jar
+in your dll (the ones that could be internally loaded by
+feature e.g. maps.jar, or you cannot embed jars that are under some
+proprietary license).
+
+Set the build action for these jars in the properties page to "InputJar".
+
+
+== Reference Jar and Embedded Reference Jar ==
+
+These are jars that are referenced by the input jar. C# bindings will
+not be created for these jars. These jars will be used to resolve
+types used by the input jar.
+
+NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
+based on the Target Framework selected.
+
+Set the build action for these jars in the properties page to "ReferenceJar".
+
+"EmbeddedJar" works like "ReferenceJar", but like "EmbeddedJar", it is
+embedded in your dll. But at application build time, they are not included
+in the final apk, like ReferenceJar files.
+
diff --git a/Lottie.Android.net6/Jars/lottie-4.2.2.aar b/Lottie.Android.net6/Jars/lottie-4.2.2.aar
new file mode 100644
index 0000000..f208ffa
Binary files /dev/null and b/Lottie.Android.net6/Jars/lottie-4.2.2.aar differ
diff --git a/Lottie.Android.net6/JavaDocs/lottie-4.2.2-sources.jar b/Lottie.Android.net6/JavaDocs/lottie-4.2.2-sources.jar
new file mode 100644
index 0000000..8a8e962
Binary files /dev/null and b/Lottie.Android.net6/JavaDocs/lottie-4.2.2-sources.jar differ
diff --git a/Lottie.Android.net6/Lottie.Android.net6.csproj b/Lottie.Android.net6/Lottie.Android.net6.csproj
new file mode 100644
index 0000000..4012a4d
--- /dev/null
+++ b/Lottie.Android.net6/Lottie.Android.net6.csproj
@@ -0,0 +1,32 @@
+
+
+ net6.0-android
+ Lottie.Android
+ Lottie.Android
+ Render After Effects animations natively on Android, iOS, MacOS, TVOs and UWP
+ Com.Airbnb.Android.Lottie
+ true
+ false
+ 6.0.4
+
+ XAJavaInterop1
+ <_EnableInterfaceMembers>true
+ true
+ d8
+ r8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lottie.Android.net6/Lottie.Android.net6.sln b/Lottie.Android.net6/Lottie.Android.net6.sln
new file mode 100644
index 0000000..9705ad2
--- /dev/null
+++ b/Lottie.Android.net6/Lottie.Android.net6.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 25.0.1700.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lottie.Android", "Lottie.Android.net6.csproj", "{C3DB3049-F830-4FB1-844F-3379005C7C78}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {BA8370AE-4B2B-429F-9ADA-65BF99FFB691}
+ EndGlobalSection
+EndGlobal
diff --git a/Lottie.Android.net6/Transforms/EnumFields.xml b/Lottie.Android.net6/Transforms/EnumFields.xml
new file mode 100644
index 0000000..68cc7fd
--- /dev/null
+++ b/Lottie.Android.net6/Transforms/EnumFields.xml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/Lottie.Android.net6/Transforms/EnumMethods.xml b/Lottie.Android.net6/Transforms/EnumMethods.xml
new file mode 100644
index 0000000..bd77573
--- /dev/null
+++ b/Lottie.Android.net6/Transforms/EnumMethods.xml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/Lottie.Android.net6/Transforms/Metadata.xml b/Lottie.Android.net6/Transforms/Metadata.xml
new file mode 100644
index 0000000..8f46580
--- /dev/null
+++ b/Lottie.Android.net6/Transforms/Metadata.xml
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ public
+
+ public
+ canvas
+ parentMatrix
+ alpha
+
+ outBounds
+ parentMatrix
+ applyParents
+
+ public
+ contentsBefore
+ contentsAfter
+
+ public
+ contentsBefore
+ contentsAfter
+
+
+ attrs
+ context
+
+
+ public
+ public
+ public
+
+
+
+
+ defStyleAttr
+
+ assetDelegate
+
+ animationName
+
+ animationName
+ cacheStrategy
+
+ json
+ animationResId
+
+ animationResId
+ cacheStrategy
+
+ updateListener
+ updateListener
+
+ listener
+ listener
+
+ loop
+
+ composition
+
+
+ imageAssetsFolder
+ speed
+
+ name
+
+ composition
+
+ enable
+
+ assetDelegate
+ enabled
+ textDelegate
+
+ id
+ bitmap
+
+
+ startProgress
+ startFrame
+
+ endFrame
+ endProgress
+
+ minFrame
+ maxFrame
+
+ minProgress
+ maxProgress
+
+
+ enabled
+
+ fileName
+
+
+ fileName
+ listener
+
+
+ stream
+ listener
+
+
+ jsonString
+ loadedListener
+
+ reader
+ loadedListener
+
+
+ who
+ who
+ what
+ when
+
+ composition
+ imageAssetsFolder
+
+ whot
+ what
+
+ colorFilter
+ canvas
+ progress
+ alpha
+
+ assetDelegate
+
+ listener
+ updateListener
+
+ loop
+
+ listener
+ updateListener
+
+ speed
+
+ enable
+
+ assetDelegate
+
+ enabled
+
+ textDelegate
+
+ id
+ bitmap
+
+ startProgress
+ startFrame
+
+ endFrame
+ endProgress
+
+ minFrame
+ maxFrame
+
+ minProgress
+ maxProgress
+
+ composition
+
+ id
+ fontFamily
+ style
+
+
+ color
+
+
+ drawable
+ animationView
+ input
+ input
+ output
+
+ input
+ cacheText
+
+
+ asset
+
+
+
+ contentsBefore
+ contentsAfter
+
+ outBounds
+ parentMatrix
+
+ contentsIter
+
+ canvas
+ parentMatrix
+ alpha
+
+
+ fontFamily
+ fontFamily
+
+
+
+ frameInfo
+
+
+ property
+ callback
+
+ keyPath
+ depth
+ accumulator
+ currentPartialKeyPath
+
\ No newline at end of file
diff --git a/Lottie.Android.net6/readme.txt b/Lottie.Android.net6/readme.txt
new file mode 100644
index 0000000..5831419
--- /dev/null
+++ b/Lottie.Android.net6/readme.txt
@@ -0,0 +1,22 @@
+---------------------------------
+Lottie
+---------------------------------
+
+Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as json with Bodymovin and renders them natively on mobile!
+
+Using Lottie on Xamarin.Android:
+
+
+
+---------------------------------
+Star on Github if this project helps you: https://github.com/Baseflow/LottieXamarin
+
+Commercial support is available. Integration with your app or services, samples, feature request, etc. Email: hello@baseflow.com
+Powered by: https://baseflow.com
+---------------------------------
\ No newline at end of file
diff --git a/Lottie.Android/Lottie.Android.sln b/Lottie.Android/Lottie.Android.sln
new file mode 100644
index 0000000..2e8aa44
--- /dev/null
+++ b/Lottie.Android/Lottie.Android.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 25.0.1700.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lottie.Android", "Lottie.Android.csproj", "{C3DB3049-F830-4FB1-844F-3379005C7C78}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3DB3049-F830-4FB1-844F-3379005C7C78}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {BA8370AE-4B2B-429F-9ADA-65BF99FFB691}
+ EndGlobalSection
+EndGlobal
diff --git a/Lottie.Maui/AnimationSource.cs b/Lottie.Maui/AnimationSource.cs
new file mode 100644
index 0000000..b94a3e7
--- /dev/null
+++ b/Lottie.Maui/AnimationSource.cs
@@ -0,0 +1,33 @@
+namespace Lottie.Maui
+{
+ public enum AnimationSource
+ {
+ ///
+ /// Use when Lottie should load the json file from Asset or Bundle folder.
+ /// The Animation input should be a string containing the file name
+ ///
+ AssetOrBundle,
+
+ ///
+ /// Url should point to a json file containing the Lottie animation on a remote resource
+ ///
+ Url,
+
+ ///
+ /// Use when passing in json directly as a string
+ ///
+ Json,
+
+ ///
+ /// Stream the Lottie animation to the view
+ ///
+ Stream,
+
+ ///
+ /// When loading from an EmbeddedResource which is compiled into an Assembly
+ /// Either set the file name as string to make Lottie read from the calling Assembly
+ /// Or use the syntax "resource://LottieLogo1.json?assembly=Example.Forms"
+ ///
+ EmbeddedResource
+ }
+}
diff --git a/Lottie.Maui/AnimationView.cs b/Lottie.Maui/AnimationView.cs
new file mode 100644
index 0000000..9db25de
--- /dev/null
+++ b/Lottie.Maui/AnimationView.cs
@@ -0,0 +1,480 @@
+using System.Reflection;
+using System.Windows.Input;
+
+namespace Lottie.Maui
+{
+ public class AnimationView : View
+ {
+ //public static readonly BindableProperty ImageProperty = BindableProperty.Create(nameof(Image),
+ // typeof(ImageSource), typeof(AnimationView), default(ImageSource));
+
+ public static readonly BindableProperty AnimationProperty = BindableProperty.Create(nameof(Animation),
+ typeof(object), typeof(AnimationView), default(object));
+
+ public static readonly BindableProperty AnimationSourceProperty = BindableProperty.Create(nameof(Maui.AnimationSource),
+ typeof(AnimationSource), typeof(AnimationView), AnimationSource.AssetOrBundle);
+
+ public static readonly BindableProperty CacheCompositionProperty = BindableProperty.Create(nameof(CacheComposition),
+ typeof(bool), typeof(AnimationView), true);
+
+ public static readonly BindableProperty FallbackResourceProperty = BindableProperty.Create(nameof(FallbackResource),
+ typeof(ImageSource), typeof(AnimationView), default(ImageSource));
+
+ //public static readonly BindableProperty CompositionProperty = BindableProperty.Create(nameof(Composition),
+ // typeof(ILottieComposition), typeof(AnimationView), default(ILottieComposition));
+
+ public static readonly BindableProperty MinFrameProperty = BindableProperty.Create(nameof(MinFrame),
+ typeof(int), typeof(AnimationView), int.MinValue);
+
+ public static readonly BindableProperty MinProgressProperty = BindableProperty.Create(nameof(MinProgress),
+ typeof(float), typeof(AnimationView), float.MinValue);
+
+ public static readonly BindableProperty MaxFrameProperty = BindableProperty.Create(nameof(MaxFrame),
+ typeof(int), typeof(AnimationView), int.MinValue);
+
+ public static readonly BindableProperty MaxProgressProperty = BindableProperty.Create(nameof(MaxProgress),
+ typeof(float), typeof(AnimationView), float.MinValue);
+
+ public static readonly BindableProperty SpeedProperty = BindableProperty.Create(nameof(Speed),
+ typeof(float), typeof(AnimationView), 1.0f);
+
+ public static readonly BindableProperty RepeatModeProperty = BindableProperty.Create(nameof(RepeatMode),
+ typeof(RepeatMode), typeof(AnimationView), Lottie.Maui.RepeatMode.Restart);
+
+ public static readonly BindableProperty RepeatCountProperty = BindableProperty.Create(nameof(RepeatCount),
+ typeof(int), typeof(AnimationView), 0);
+
+ public static readonly BindableProperty IsAnimatingProperty = BindableProperty.Create(nameof(IsAnimating),
+ typeof(bool), typeof(AnimationView), false);
+
+ public static readonly BindableProperty ImageAssetsFolderProperty = BindableProperty.Create(nameof(ImageAssetsFolder),
+ typeof(string), typeof(AnimationView), default(string));
+
+ //public static new readonly BindableProperty ScaleProperty = BindableProperty.Create(nameof(Scale),
+ // typeof(float), typeof(AnimationView), 1.0f);
+
+ public static readonly BindableProperty FrameProperty = BindableProperty.Create(nameof(Frame),
+ typeof(int), typeof(AnimationView), default(int));
+
+ public static readonly BindableProperty ProgressProperty = BindableProperty.Create(nameof(Progress),
+ typeof(float), typeof(AnimationView), 0.0f);
+
+ //TODO: Maybe make TimeSpan
+ public static readonly BindableProperty DurationProperty = BindableProperty.Create(nameof(Duration),
+ typeof(long), typeof(AnimationView), default(long));
+
+ public static readonly BindableProperty AutoPlayProperty = BindableProperty.Create(nameof(AutoPlay),
+ typeof(bool), typeof(AnimationView), true);
+
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),
+ typeof(ICommand), typeof(AnimationView));
+
+ public static readonly BindableProperty EnableMergePathsForKitKatAndAboveProperty = BindableProperty.Create(nameof(EnableMergePathsForKitKatAndAbove),
+ typeof(bool), typeof(AnimationView), false);
+
+ ///
+ /// Returns the duration of an animation (Frames / FrameRate * 1000)
+ ///
+ public long Duration
+ {
+ get { return (long)GetValue(DurationProperty); }
+ internal set { SetValue(DurationProperty, value); }
+ }
+
+ ///
+ /// Indicates if a Lottie Animation should be cached
+ ///
+ public bool CacheComposition
+ {
+ get { return (bool)GetValue(CacheCompositionProperty); }
+ set { SetValue(CacheCompositionProperty, value); }
+ }
+
+ ///
+ /// Set the Animation that you want to play. This can be a URL (either local path or remote), Json string, or Stream
+ ///
+ public object Animation
+ {
+ get { return (object)GetValue(AnimationProperty); }
+ set { SetValue(AnimationProperty, value); }
+ }
+
+ ///
+ /// Indicates where the Animation is located and from which source it should be loaded
+ /// Default value is AssetOrBundle
+ ///
+ public AnimationSource AnimationSource
+ {
+ get { return (AnimationSource)GetValue(AnimationSourceProperty); }
+ set { SetValue(AnimationSourceProperty, value); }
+ }
+
+ ///
+ /// Used in case an animations fails to load
+ ///
+ public ImageSource FallbackResource
+ {
+ get { return (ImageSource)GetValue(FallbackResourceProperty); }
+ set { SetValue(FallbackResourceProperty, value); }
+ }
+
+ //public ILottieComposition Composition
+ //{
+ // get { return (ILottieComposition)GetValue(CompositionProperty); }
+ // set { SetValue(CompositionProperty, value); }
+ //}
+
+ ///
+ /// Sets or gets the minimum frame that the animation will start from when playing or looping.
+ ///
+ public int MinFrame
+ {
+ get { return (int)GetValue(MinFrameProperty); }
+ set { SetValue(MinFrameProperty, value); }
+ }
+
+ ///
+ /// Sets or gets the minimum progress that the animation will start from when playing or looping.
+ ///
+ public float MinProgress
+ {
+ get { return (float)GetValue(MinProgressProperty); }
+ set { SetValue(MinProgressProperty, value); }
+ }
+
+ ///
+ /// Sets or gets the maximum frame that the animation will end at when playing or looping.
+ ///
+ public int MaxFrame
+ {
+ get { return (int)GetValue(MaxFrameProperty); }
+ set { SetValue(MaxFrameProperty, value); }
+ }
+
+ ///
+ /// Sets or gets the maximum progress that the animation will end at when playing or looping.
+ ///
+ public float MaxProgress
+ {
+ get { return (float)GetValue(MaxProgressProperty); }
+ set { SetValue(MaxProgressProperty, value); }
+ }
+
+ ///
+ /// Returns the current playback speed. This will be < 0 if the animation is playing backwards.
+ ///
+ public float Speed
+ {
+ get { return (float)GetValue(SpeedProperty); }
+ set { SetValue(SpeedProperty, value); }
+ }
+
+ ///
+ /// Defines what this animation should do when it reaches the end.
+ /// This setting is applied only when the repeat count is either greater than 0 or INFINITE.
+ /// Defaults to RESTART.
+ ///
+ public RepeatMode RepeatMode
+ {
+ get { return (RepeatMode)GetValue(RepeatModeProperty); }
+ set { SetValue(RepeatModeProperty, value); }
+ }
+
+ ///
+ /// Sets how many times the animation should be repeated. If the repeat count is 0, the animation is never repeated.
+ /// If the repeat count is greater than 0 or INFINITE, the repeat mode will be taken into account.
+ /// The repeat count is 0 by default.
+ ///
+ public int RepeatCount
+ {
+ get { return (int)GetValue(RepeatCountProperty); }
+ set { SetValue(RepeatCountProperty, value); }
+ }
+
+ ///
+ /// Indicates if the Animation is playing
+ ///
+ public bool IsAnimating
+ {
+ get { return (bool)GetValue(IsAnimatingProperty); }
+ internal set { SetValue(IsAnimatingProperty, value); }
+ }
+
+ ///
+ /// If you use image assets, you must explicitly specify the folder in assets/ in which they are located because bodymovin uses the name filenames across all compositions
+ ///
+ public string ImageAssetsFolder
+ {
+ get { return (string)GetValue(ImageAssetsFolderProperty); }
+ set { SetValue(ImageAssetsFolderProperty, value); }
+ }
+
+ ///
+ /// Set the scale on the current composition.
+ /// The only cost of this function is re-rendering the current frame so you may call it frequent to scale something up or down.
+ ///
+ //public new float Scale
+ //{
+ // get { return (float)GetValue(ScaleProperty); }
+ // set { SetValue(ScaleProperty, value); }
+ //}
+
+ ///
+ /// Sets the progress to the specified frame.
+ /// If the composition isn't set yet, the progress will be set to the frame when it is.
+ ///
+ public int Frame
+ {
+ get { return (int)GetValue(FrameProperty); }
+ set { SetValue(FrameProperty, value); }
+ }
+
+ ///
+ /// Returns the current progress of the animation
+ ///
+ public float Progress
+ {
+ get { return (float)GetValue(ProgressProperty); }
+ set { SetValue(ProgressProperty, value); }
+ }
+
+ ///
+ /// When true the Lottie animation will automatically start playing when loaded
+ ///
+ public bool AutoPlay
+ {
+ get { return (bool)GetValue(AutoPlayProperty); }
+ set { SetValue(AutoPlayProperty, value); }
+ }
+
+ ///
+ /// Will be called when the view is clicked
+ ///
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ ///
+ /// When true the Lottie animation will enable merge paths for devices with KitKat and above
+ ///
+ public bool EnableMergePathsForKitKatAndAbove
+ {
+ get { return (bool)GetValue(EnableMergePathsForKitKatAndAboveProperty); }
+ set { SetValue(EnableMergePathsForKitKatAndAboveProperty, value); }
+ }
+
+ ///
+ /// Called when the Lottie animation starts playing
+ ///
+ public event EventHandler OnPlayAnimation;
+
+ ///
+ /// Called when the Lottie animation is paused
+ ///
+ public event EventHandler OnPauseAnimation;
+
+ ///
+ /// Called when the Lottie animation is resumed after pausing
+ ///
+ public event EventHandler OnResumeAnimation;
+
+ ///
+ /// Called when the Lottie animation is stopped
+ ///
+ public event EventHandler OnStopAnimation;
+
+ ///
+ /// Called when the Lottie animation is repeated
+ ///
+ public event EventHandler OnRepeatAnimation;
+
+ ///
+ /// Called when the Lottie animation is clicked
+ ///
+ public event EventHandler Clicked;
+
+ ///
+ /// Called when the Lottie animation is playing with the current progress
+ ///
+ public event EventHandler OnAnimationUpdate;
+
+ ///
+ /// Called when the Lottie animation is loaded with the Lottie Composition as parameter
+ ///
+ public event EventHandler OnAnimationLoaded;
+
+ ///
+ /// Called when the animation fails to load or when an exception happened when trying to play
+ ///
+ public event EventHandler OnFailure;
+
+ ///
+ /// Called when the Lottie animation is finished playing
+ ///
+ public event EventHandler OnFinishedAnimation;
+
+ internal void InvokePlayAnimation()
+ {
+ OnPlayAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokeResumeAnimation()
+ {
+ OnResumeAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokeStopAnimation()
+ {
+ OnStopAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokePauseAnimation()
+ {
+ OnPauseAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokeRepeatAnimation()
+ {
+ OnRepeatAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokeAnimationUpdate(float progress)
+ {
+ OnAnimationUpdate?.Invoke(this, progress);
+ }
+
+ internal void InvokeAnimationLoaded(object animation)
+ {
+ OnAnimationLoaded?.Invoke(this, animation);
+ }
+
+ internal void InvokeFailure(Exception ex)
+ {
+ OnFailure?.Invoke(this, ex);
+ }
+
+ internal void InvokeFinishedAnimation()
+ {
+ OnFinishedAnimation?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal void InvokeClick()
+ {
+ Clicked?.Invoke(this, EventArgs.Empty);
+ Command.ExecuteCommandIfPossible(this);
+ }
+
+ internal ICommand PlayCommand { get; set; }
+ internal ICommand PauseCommand { get; set; }
+ internal ICommand ResumeCommand { get; set; }
+ internal ICommand StopCommand { get; set; }
+ internal ICommand ClickCommand { get; set; }
+ internal ICommand PlayMinAndMaxFrameCommand { get; set; }
+ internal ICommand PlayMinAndMaxProgressCommand { get; set; }
+ internal ICommand ReverseAnimationSpeedCommand { get; set; }
+
+ ///
+ /// Simulate a click action on the view
+ ///
+ public void Click()
+ {
+ ClickCommand.ExecuteCommandIfPossible(this);
+ }
+
+ ///
+ /// Plays the animation from the beginning. If speed is < 0, it will start at the end and play towards the beginning
+ ///
+ public void PlayAnimation()
+ {
+ PlayCommand.ExecuteCommandIfPossible();
+ }
+
+ ///
+ /// Continues playing the animation from its current position. If speed < 0, it will play backwards from the current position.
+ ///
+ public void ResumeAnimation()
+ {
+ ResumeCommand.ExecuteCommandIfPossible();
+ }
+
+ ///
+ /// Will stop and reset the currently playing animation
+ ///
+ public void StopAnimation()
+ {
+ StopCommand.ExecuteCommandIfPossible();
+ }
+
+ ///
+ /// Will pause the currently playing animation. Call ResumeAnimation to continue
+ ///
+ public void PauseAnimation()
+ {
+ PauseCommand.ExecuteCommandIfPossible();
+ }
+
+ public void PlayMinAndMaxFrame(int minFrame, int maxFrame)
+ {
+ PlayMinAndMaxFrameCommand.ExecuteCommandIfPossible((minFrame, maxFrame));
+ }
+
+ public void PlayMinAndMaxProgress(float minProgress, float maxProgress)
+ {
+ PlayMinAndMaxProgressCommand.ExecuteCommandIfPossible((minProgress, maxProgress));
+ }
+
+ ///
+ /// Reverses the current animation speed. This does NOT play the animation.
+ ///
+ public void ReverseAnimationSpeed()
+ {
+ ReverseAnimationSpeedCommand.ExecuteCommandIfPossible();
+ }
+
+ public void SetAnimationFromAssetOrBundle(string path)
+ {
+ AnimationSource = AnimationSource.AssetOrBundle;
+ Animation = path;
+ }
+
+ public void SetAnimationFromEmbeddedResource(string resourceName, Assembly assembly = null)
+ {
+ AnimationSource = AnimationSource.EmbeddedResource;
+
+ if (assembly == null)
+ assembly = Application.Current.GetType().Assembly;
+
+ Animation = $"resource://{resourceName}?assembly={Uri.EscapeUriString(assembly.FullName)}";
+ }
+
+ public void SetAnimationFromJson(string json)
+ {
+ AnimationSource = AnimationSource.Json;
+ Animation = json;
+ }
+
+ public void SetAnimationFromUrl(string url)
+ {
+ AnimationSource = AnimationSource.Url;
+ Animation = url;
+ }
+
+ public void SetAnimationFromStream(Stream stream)
+ {
+ AnimationSource = AnimationSource.Stream;
+ Animation = stream;
+ }
+
+ // setImageAssetDelegate(ImageAssetDelegate assetDelegate) {
+
+ // setFontAssetDelegate(
+
+ // setTextDelegate(TextDelegate textDelegate)
+
+ // setScaleType
+
+ //RenderMode
+ }
+}
diff --git a/Lottie.Maui/AnimationViewExtensions.cs b/Lottie.Maui/AnimationViewExtensions.cs
new file mode 100644
index 0000000..7fb8078
--- /dev/null
+++ b/Lottie.Maui/AnimationViewExtensions.cs
@@ -0,0 +1,56 @@
+using System.Reflection;
+
+namespace Lottie.Maui
+{
+ public static class AnimationViewExtensions
+ {
+ public static Stream GetStreamFromAssembly(this AnimationView animationView)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ if (animationView.Animation is string embeddedAnimation)
+ {
+ Assembly assembly = null;
+ string resourceName = null;
+
+ if (embeddedAnimation.StartsWith("resource://", StringComparison.OrdinalIgnoreCase))
+ {
+ var uri = new Uri(embeddedAnimation);
+
+ var parts = uri.OriginalString.Substring(11).Split('?');
+ resourceName = parts.First();
+
+ if (parts.Length > 1)
+ {
+ var name = Uri.UnescapeDataString(uri.Query.Substring(10));
+ var assemblyName = new AssemblyName(name);
+ assembly = Assembly.Load(assemblyName);
+ }
+
+ if (assembly == null)
+ {
+ var callingAssemblyMethod = typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetCallingAssembly");
+ assembly = (Assembly)callingAssemblyMethod.Invoke(null, Array.Empty());
+ }
+ }
+
+ if (assembly == null)
+ assembly = Application.Current.GetType().Assembly;
+
+ if (string.IsNullOrEmpty(resourceName))
+ resourceName = embeddedAnimation;
+
+ var stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{resourceName}");
+
+ if (stream == null)
+ {
+ return null;
+ //throw new FileNotFoundException("Cannot find file.", embeddedAnimation);
+ }
+ return stream;
+ }
+ return null;
+ }
+ }
+}
diff --git a/Lottie.Maui/ILottieComposition.cs b/Lottie.Maui/ILottieComposition.cs
new file mode 100644
index 0000000..8ea452c
--- /dev/null
+++ b/Lottie.Maui/ILottieComposition.cs
@@ -0,0 +1,7 @@
+namespace Lottie.Maui
+{
+ public interface ILottieComposition : IDisposable
+ {
+ //TODO: Implement native per platform
+ }
+}
diff --git a/Lottie.Maui/Lottie.Maui.csproj b/Lottie.Maui/Lottie.Maui.csproj
new file mode 100644
index 0000000..16d9a1e
--- /dev/null
+++ b/Lottie.Maui/Lottie.Maui.csproj
@@ -0,0 +1,38 @@
+
+
+ net6.0;net6.0-android;net6.0-ios
+ enable
+ true
+
+
+
+ Lottie.Maui
+ Lottie.Maui
+ Render After Effects animations natively on Android and iOS.
+ Com.Airbnb.Maui.Lottie
+ 3.0.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lottie.Maui/Lottie.Maui.sln b/Lottie.Maui/Lottie.Maui.sln
new file mode 100644
index 0000000..fe8a173
--- /dev/null
+++ b/Lottie.Maui/Lottie.Maui.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 25.0.1700.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lottie.Maui", "Lottie.Maui.csproj", "{99BD63A0-8C06-4BFE-944A-04231666B35B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {99BD63A0-8C06-4BFE-944A-04231666B35B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99BD63A0-8C06-4BFE-944A-04231666B35B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99BD63A0-8C06-4BFE-944A-04231666B35B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99BD63A0-8C06-4BFE-944A-04231666B35B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DDB5F21F-5C0B-4651-8FBE-AA7C9EE9FF24}
+ EndGlobalSection
+EndGlobal
diff --git a/Lottie.Maui/LottieExtensions.cs b/Lottie.Maui/LottieExtensions.cs
new file mode 100644
index 0000000..bf880ce
--- /dev/null
+++ b/Lottie.Maui/LottieExtensions.cs
@@ -0,0 +1,15 @@
+using System.Windows.Input;
+
+namespace Lottie.Maui
+{
+ public static class LottieExtensions
+ {
+ public static void ExecuteCommandIfPossible(this ICommand command, object parameter = null)
+ {
+ if (command?.CanExecute(parameter) == true)
+ {
+ command.Execute(parameter);
+ }
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Android/AnimationViewExtensions.cs
new file mode 100644
index 0000000..e889c9f
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/AnimationViewExtensions.cs
@@ -0,0 +1,97 @@
+using System.IO;
+using Com.Airbnb.Lottie;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public static class AnimationViewExtensions
+ {
+ public static void TrySetAnimation(this LottieAnimationView lottieAnimationView, AnimationView animationView)
+ {
+ if (lottieAnimationView == null)
+ throw new ArgumentNullException(nameof(lottieAnimationView));
+
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ lottieAnimationView.TrySetAnimation(animationView, animationView.Animation);
+ break;
+ case AnimationSource.Url:
+ if (animationView.Animation is string stringAnimation)
+ lottieAnimationView.SetAnimationFromUrl(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ if (animationView.Animation is string jsonAnimation)
+ lottieAnimationView.SetAnimationFromJson(jsonAnimation);
+ break;
+ case AnimationSource.Stream:
+ lottieAnimationView.TrySetAnimation(animationView, animationView.Animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ lottieAnimationView.TrySetAnimation(animationView, animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ }
+
+ public static void TrySetAnimation(this LottieAnimationView lottieAnimationView, AnimationView animationView, object animation)
+ {
+ if (lottieAnimationView == null)
+ throw new ArgumentNullException(nameof(lottieAnimationView));
+
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ switch (animation)
+ {
+ case int intAnimation:
+ lottieAnimationView.SetAnimation(intAnimation);
+ break;
+ case string stringAnimation:
+
+ //TODO: check if json
+ //animationView.SetAnimationFromJson(stringAnimation);
+ //TODO: check if url
+ //animationView.SetAnimationFromUrl(stringAnimation);
+
+ lottieAnimationView.SetAnimation(stringAnimation);
+ break;
+ case Stream streamAnimation:
+ lottieAnimationView.SetAnimation(streamAnimation, null);
+ break;
+ case null:
+ lottieAnimationView.ClearAnimation();
+ break;
+ default:
+ break;
+ }
+ }
+
+ public static void ConfigureRepeat(this LottieAnimationView lottieAnimationView, RepeatMode repeatMode, int repeatCount)
+ {
+ if (lottieAnimationView == null)
+ throw new ArgumentNullException(nameof(lottieAnimationView));
+
+ lottieAnimationView.RepeatCount = repeatCount;
+
+ switch (repeatMode)
+ {
+ case RepeatMode.Infinite:
+ {
+ lottieAnimationView.RepeatCount = int.MaxValue;
+ lottieAnimationView.RepeatMode = LottieDrawable.Infinite;
+ break;
+ }
+ case RepeatMode.Restart:
+ lottieAnimationView.RepeatMode = LottieDrawable.Restart;
+ break;
+ case RepeatMode.Reverse:
+ lottieAnimationView.RepeatMode = LottieDrawable.Reverse;
+ break;
+ }
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Android/AnimationViewRenderer.cs
new file mode 100644
index 0000000..5bb84eb
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/AnimationViewRenderer.cs
@@ -0,0 +1,213 @@
+using System.ComponentModel;
+using Android.Runtime;
+using Lottie.Maui;
+using Lottie.Maui.Platforms.Android;
+using Microsoft.Maui.Controls.Compatibility;
+using Microsoft.Maui.Controls.Platform;
+using Com.Airbnb.Lottie;
+using Android.Content;
+
+[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer))]
+#if MAUI
+Preserve(AllMembers = true)
+#endif
+namespace Lottie.Maui.Platforms.Android
+{
+#pragma warning disable 0618
+ public class AnimationViewRenderer : Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat.ViewRenderer
+ {
+ private LottieAnimationView _animationView;
+ private AnimatorListener _animatorListener;
+ private AnimatorUpdateListener _animatorUpdateListener;
+ private LottieOnCompositionLoadedListener _lottieOnCompositionLoadedListener;
+ private LottieFailureListener _lottieFailureListener;
+ private ClickListener _clickListener;
+
+ public AnimationViewRenderer(Context context) : base(context) {}
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ _animationView.RemoveAnimatorListener(_animatorListener);
+ _animationView.RemoveAllUpdateListeners();
+ _animationView.RemoveLottieOnCompositionLoadedListener(_lottieOnCompositionLoadedListener);
+ _animationView.SetFailureListener(null);
+ _animationView.SetOnClickListener(null);
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationView = new LottieAnimationView(Context);
+ _animatorListener = new AnimatorListener
+ {
+ OnAnimationCancelImpl = () => e.NewElement.InvokeStopAnimation(),
+ OnAnimationEndImpl = () => e.NewElement.InvokeFinishedAnimation(),
+ OnAnimationPauseImpl = () => e.NewElement.InvokePauseAnimation(),
+ OnAnimationRepeatImpl = () => e.NewElement.InvokeRepeatAnimation(),
+ OnAnimationResumeImpl = () => e.NewElement.InvokeResumeAnimation(),
+ OnAnimationStartImpl = () => e.NewElement.InvokePlayAnimation()
+ };
+ _animatorUpdateListener = new AnimatorUpdateListener
+ {
+ OnAnimationUpdateImpl = (progress) => e.NewElement.InvokeAnimationUpdate(progress)
+ };
+ _lottieOnCompositionLoadedListener = new LottieOnCompositionLoadedListener
+ {
+ OnCompositionLoadedImpl = (composition) => e.NewElement.InvokeAnimationLoaded(composition)
+ };
+ _lottieFailureListener = new LottieFailureListener
+ {
+ OnResultImpl = (exception) => e.NewElement.InvokeFailure(exception)
+ };
+ _clickListener = new ClickListener
+ {
+ OnClickImpl = () => e.NewElement.InvokeClick()
+ };
+
+ _animationView.AddAnimatorListener(_animatorListener);
+ _animationView.AddAnimatorUpdateListener(_animatorUpdateListener);
+ _animationView.AddLottieOnCompositionLoadedListener(_lottieOnCompositionLoadedListener);
+ _animationView.SetFailureListener(_lottieFailureListener);
+ _animationView.SetOnClickListener(_clickListener);
+
+ _animationView.TrySetAnimation(e.NewElement);
+
+ e.NewElement.PlayCommand = new Command(() => _animationView.PlayAnimation());
+ e.NewElement.PauseCommand = new Command(() => _animationView.PauseAnimation());
+ e.NewElement.ResumeCommand = new Command(() => _animationView.ResumeAnimation());
+ e.NewElement.StopCommand = new Command(() =>
+ {
+ _animationView.CancelAnimation();
+ _animationView.Progress = 0.0f;
+ });
+ e.NewElement.ClickCommand = new Command(() => _animationView.PerformClick());
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ {
+ _animationView.SetMinAndMaxFrame(minFrame, maxFrame);
+ _animationView.PlayAnimation();
+ }
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ {
+ _animationView.SetMinAndMaxProgress(minProgress, maxProgress);
+ _animationView.PlayAnimation();
+ }
+ });
+ e.NewElement.ReverseAnimationSpeedCommand = new Command(() => _animationView.ReverseAnimationSpeed());
+
+ _animationView.SetCacheComposition(e.NewElement.CacheComposition);
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ if (e.NewElement.MinFrame != int.MinValue)
+ _animationView.SetMinFrame(e.NewElement.MinFrame);
+ if (e.NewElement.MinProgress != float.MinValue)
+ _animationView.SetMinProgress(e.NewElement.MinProgress);
+ if (e.NewElement.MaxFrame != int.MinValue)
+ _animationView.SetMaxFrame(e.NewElement.MaxFrame);
+ if (e.NewElement.MaxProgress != float.MinValue)
+ _animationView.SetMaxProgress(e.NewElement.MaxProgress);
+
+ _animationView.Speed = e.NewElement.Speed;
+
+ _animationView.ConfigureRepeat(e.NewElement.RepeatMode, e.NewElement.RepeatCount);
+
+ if (!string.IsNullOrEmpty(e.NewElement.ImageAssetsFolder))
+ _animationView.ImageAssetsFolder = e.NewElement.ImageAssetsFolder;
+
+ //TODO: see if this needs to be enabled
+ //_animationView.Scale = Convert.ToSingle(e.NewElement.Scale);
+
+ _animationView.Frame = e.NewElement.Frame;
+ _animationView.Progress = e.NewElement.Progress;
+
+ _animationView.EnableMergePathsForKitKatAndAbove(e.NewElement.EnableMergePathsForKitKatAndAbove);
+
+ SetNativeControl(_animationView);
+
+ if (e.NewElement.AutoPlay || e.NewElement.IsAnimating)
+ _animationView.PlayAnimation();
+
+ e.NewElement.Duration = _animationView.Duration;
+ e.NewElement.IsAnimating = _animationView.IsAnimating;
+ }
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ _animationView.TrySetAnimation(Element);
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ _animationView.PlayAnimation();
+ }
+
+ //if (e.PropertyName == AnimationView.AutoPlayProperty.PropertyName)
+ // _animationView.AutoPlay = (Element.AutoPlay);
+
+ if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ _animationView.SetCacheComposition(Element.CacheComposition);
+
+ //if (e.PropertyName == AnimationView.FallbackResource.PropertyName)
+ // _animationView.SetFallbackResource(e.NewElement.FallbackResource);
+
+ //if (e.PropertyName == AnimationView.Composition.PropertyName)
+ // _animationView.Composition = e.NewElement.Composition;
+
+ if (e.PropertyName == AnimationView.EnableMergePathsForKitKatAndAboveProperty.PropertyName)
+ _animationView.EnableMergePathsForKitKatAndAbove(Element.EnableMergePathsForKitKatAndAbove);
+
+ if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ _animationView.SetMinFrame(Element.MinFrame);
+
+ if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ _animationView.SetMinProgress(Element.MinProgress);
+
+ if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ _animationView.SetMaxFrame(Element.MaxFrame);
+
+ if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ _animationView.SetMaxProgress(Element.MaxProgress);
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.Speed = Element.Speed;
+
+ if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName || e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ _animationView.ConfigureRepeat(Element.RepeatMode, Element.RepeatCount);
+
+ if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ //TODO: see if this needs to be enabled
+ //if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ // _animationView.Scale = Element.Scale;
+
+ if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.Progress = Element.Progress;
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+ }
+#pragma warning restore 0618
+}
diff --git a/Lottie.Maui/Platforms/Android/AnimatorListener.cs b/Lottie.Maui/Platforms/Android/AnimatorListener.cs
new file mode 100644
index 0000000..d6e36b6
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/AnimatorListener.cs
@@ -0,0 +1,59 @@
+using Android.Animation;
+using Android.Runtime;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class AnimatorListener : AnimatorListenerAdapter
+ {
+ public AnimatorListener()
+ {
+ }
+
+ public AnimatorListener(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ public Action OnAnimationCancelImpl { get; set; }
+ public Action OnAnimationEndImpl { get; set; }
+ public Action OnAnimationPauseImpl { get; set; }
+ public Action OnAnimationRepeatImpl { get; set; }
+ public Action OnAnimationResumeImpl { get; set; }
+ public Action OnAnimationStartImpl { get; set; }
+
+ public override void OnAnimationCancel(Animator animation)
+ {
+ base.OnAnimationCancel(animation);
+ OnAnimationCancelImpl?.Invoke();
+ }
+
+ public override void OnAnimationEnd(Animator animation)
+ {
+ base.OnAnimationEnd(animation);
+ OnAnimationEndImpl?.Invoke();
+ }
+
+ public override void OnAnimationPause(Animator animation)
+ {
+ base.OnAnimationPause(animation);
+ OnAnimationPauseImpl?.Invoke();
+ }
+
+ public override void OnAnimationRepeat(Animator animation)
+ {
+ base.OnAnimationRepeat(animation);
+ OnAnimationRepeatImpl?.Invoke();
+ }
+
+ public override void OnAnimationResume(Animator animation)
+ {
+ base.OnAnimationResume(animation);
+ OnAnimationResumeImpl?.Invoke();
+ }
+
+ public override void OnAnimationStart(Animator animation)
+ {
+ base.OnAnimationStart(animation);
+ OnAnimationStartImpl?.Invoke();
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/AnimatorUpdateListener.cs b/Lottie.Maui/Platforms/Android/AnimatorUpdateListener.cs
new file mode 100644
index 0000000..25bffc4
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/AnimatorUpdateListener.cs
@@ -0,0 +1,26 @@
+using Android.Animation;
+using Android.Runtime;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class AnimatorUpdateListener : Java.Lang.Object, ValueAnimator.IAnimatorUpdateListener
+ {
+ public AnimatorUpdateListener()
+ {
+ }
+
+ public AnimatorUpdateListener(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
+ {
+ }
+
+ public Action OnAnimationUpdateImpl { get; set; }
+
+ public void OnAnimationUpdate(ValueAnimator animation)
+ {
+ if (animation == null)
+ throw new ArgumentNullException(nameof(animation));
+
+ OnAnimationUpdateImpl?.Invoke(((float)animation.AnimatedValue));
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/ClickListener.cs b/Lottie.Maui/Platforms/Android/ClickListener.cs
new file mode 100644
index 0000000..03a5fe6
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/ClickListener.cs
@@ -0,0 +1,23 @@
+using Android.Runtime;
+using static Android.Views.View;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class ClickListener : Java.Lang.Object, IOnClickListener
+ {
+ public ClickListener()
+ {
+ }
+
+ public ClickListener(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
+ {
+ }
+
+ public Action OnClickImpl { get; set; }
+
+ public void OnClick(global::Android.Views.View v)
+ {
+ OnClickImpl?.Invoke();
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/LottieAndroidComposition.cs b/Lottie.Maui/Platforms/Android/LottieAndroidComposition.cs
new file mode 100644
index 0000000..2a2816b
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/LottieAndroidComposition.cs
@@ -0,0 +1,9 @@
+using Com.Airbnb.Lottie;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class LottieAndroidComposition : LottieComposition, ILottieComposition
+ {
+
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/LottieFailureListener.cs b/Lottie.Maui/Platforms/Android/LottieFailureListener.cs
new file mode 100644
index 0000000..7c60e6d
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/LottieFailureListener.cs
@@ -0,0 +1,24 @@
+using Android.Runtime;
+using Com.Airbnb.Lottie;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class LottieFailureListener : Java.Lang.Object, ILottieListener
+ {
+ public LottieFailureListener(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
+ {
+ }
+
+ public LottieFailureListener()
+ {
+ }
+
+ public Action OnResultImpl { get; set; }
+
+ public void OnResult(Java.Lang.Object p0)
+ {
+ var javaError = p0?.ToString();
+ OnResultImpl?.Invoke(new Exception(javaError));
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Android/LottieOnCompositionLoadedListener.cs b/Lottie.Maui/Platforms/Android/LottieOnCompositionLoadedListener.cs
new file mode 100644
index 0000000..8c71a46
--- /dev/null
+++ b/Lottie.Maui/Platforms/Android/LottieOnCompositionLoadedListener.cs
@@ -0,0 +1,23 @@
+using Android.Runtime;
+using Com.Airbnb.Lottie;
+
+namespace Lottie.Maui.Platforms.Android
+{
+ public class LottieOnCompositionLoadedListener : Java.Lang.Object, ILottieOnCompositionLoadedListener
+ {
+ public LottieOnCompositionLoadedListener()
+ {
+ }
+
+ public LottieOnCompositionLoadedListener(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
+ {
+ }
+
+ public Action OnCompositionLoadedImpl { get; set; }
+
+ public void OnCompositionLoaded(LottieComposition p0)
+ {
+ OnCompositionLoadedImpl?.Invoke(p0);
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Ios/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Ios/AnimationViewExtensions.cs
new file mode 100644
index 0000000..7cc29cc
--- /dev/null
+++ b/Lottie.Maui/Platforms/Ios/AnimationViewExtensions.cs
@@ -0,0 +1,96 @@
+using System.IO;
+using Airbnb.Lottie;
+using Foundation;
+using Lottie.Maui;
+
+namespace Lottie.Platforms.Ios
+{
+ public static class AnimationViewExtensions
+ {
+ public static LOTComposition GetAnimation(this AnimationView animationView)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ var animation = animationView.Animation;
+
+ LOTComposition composition = null;
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ if (animation is string bundleAnimation)
+ {
+ if (!string.IsNullOrEmpty(animationView.ImageAssetsFolder))
+ {
+ var bundle = NSBundle.FromPath(animationView.ImageAssetsFolder);
+ if (bundle != null)
+ composition = LOTComposition.AnimationNamed(bundleAnimation, bundle);
+ }
+ else
+ composition = LOTComposition.AnimationNamed(bundleAnimation);
+ }
+ break;
+ case AnimationSource.Url:
+ if (animation is string stringAnimation)
+ composition = LOTComposition.AnimationNamed(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ if (animation is string jsonAnimation)
+ {
+ NSData objectData = NSData.FromString(jsonAnimation);
+ NSDictionary jsonData = (NSDictionary)NSJsonSerialization.Deserialize(objectData, NSJsonReadingOptions.MutableContainers, out _);
+ if (jsonData != null)
+ composition = LOTComposition.AnimationFromJSON(jsonData);
+ }
+ else if (animation is NSDictionary dictAnimation)
+ composition = LOTComposition.AnimationFromJSON(dictAnimation);
+ break;
+ case AnimationSource.Stream:
+ composition = animationView.GetAnimation(animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ composition = animationView.GetAnimation(animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+
+ public static LOTComposition GetAnimation(this AnimationView animationView, object animation)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ LOTComposition composition = null;
+ switch (animation)
+ {
+ case string stringAnimation:
+
+ //TODO: check if json
+ //animationView.SetAnimationFromJson(stringAnimation);
+ //TODO: check if url
+ //animationView.SetAnimationFromUrl(stringAnimation);
+
+ composition = LOTComposition.AnimationNamed(stringAnimation);
+ break;
+ case Stream streamAnimation:
+ using (StreamReader reader = new StreamReader(streamAnimation))
+ {
+ string json = reader.ReadToEnd();
+ NSData objectData = NSData.FromString(json);
+ NSDictionary jsonData = (NSDictionary)NSJsonSerialization.Deserialize(objectData, NSJsonReadingOptions.MutableContainers, out _);
+ if (jsonData != null)
+ composition = LOTComposition.AnimationFromJSON(jsonData);
+ }
+ break;
+ case null:
+ composition = null;
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Ios/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Ios/AnimationViewRenderer.cs
new file mode 100644
index 0000000..ec97282
--- /dev/null
+++ b/Lottie.Maui/Platforms/Ios/AnimationViewRenderer.cs
@@ -0,0 +1,236 @@
+using System.ComponentModel;
+using Airbnb.Lottie;
+using Foundation;
+using Lottie.Maui;
+using Microsoft.Maui.Controls.Compatibility.Platform.iOS;
+using Microsoft.Maui.Controls.Platform;
+using UIKit;
+
+//[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer)), Preserve(AllMembers = true)]
+
+namespace Lottie.Platforms.Ios
+{
+ public class AnimationViewRenderer : ViewRenderer
+ {
+ private LOTAnimationCompletionBlock _animationCompletionBlock;
+ private LOTAnimationView _animationView;
+ private UITapGestureRecognizer _gestureRecognizer;
+ private int repeatCount = 1;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ CleanupResources();
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationCompletionBlock = new LOTAnimationCompletionBlock(AnimationCompletionBlock);
+
+ _animationView = new LOTAnimationView()
+ {
+ AutoresizingMask = UIViewAutoresizing.All,
+ ContentMode = UIViewContentMode.ScaleAspectFit,
+ LoopAnimation = e.NewElement.RepeatMode == RepeatMode.Infinite,
+ AnimationSpeed = e.NewElement.Speed,
+ AnimationProgress = e.NewElement.Progress,
+ CacheEnable = e.NewElement.CacheComposition,
+ CompletionBlock = _animationCompletionBlock
+ };
+
+ var composition = e.NewElement.GetAnimation();
+ _animationView.SceneModel = composition;
+ e.NewElement.InvokeAnimationLoaded(composition);
+
+ e.NewElement.PlayCommand = new Command(() =>
+ {
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ e.NewElement.InvokePlayAnimation();
+ });
+ e.NewElement.PauseCommand = new Command(() =>
+ {
+ _animationView.Pause();
+ e.NewElement.InvokePauseAnimation();
+ });
+ e.NewElement.ResumeCommand = new Command(() =>
+ {
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ e.NewElement.InvokeResumeAnimation();
+ });
+ e.NewElement.StopCommand = new Command(() =>
+ {
+ _animationView.Stop();
+ e.NewElement.InvokeStopAnimation();
+ });
+ e.NewElement.ClickCommand = new Command(() =>
+ {
+ //_animationView.Click();
+ //e.NewElement.InvokeClick();
+ });
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ _animationView.PlayFromFrame(NSNumber.FromInt32(minFrame), NSNumber.FromInt32(maxFrame), AnimationCompletionBlock);
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ _animationView.PlayFromProgress(minProgress, maxProgress, AnimationCompletionBlock);
+ });
+ e.NewElement.ReverseAnimationSpeedCommand = new Command(() => _animationView.AutoReverseAnimation = !_animationView.AutoReverseAnimation);
+
+ _animationView.CacheEnable = e.NewElement.CacheComposition;
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ //TODO: makes animation stop with current default values
+ //_animationView.SetMinFrame(e.NewElement.MinFrame);
+ //_animationView.SetMinProgress(e.NewElement.MinProgress);
+ //_animationView.SetMaxFrame(e.NewElement.MaxFrame);
+ //_animationView.SetMaxProgress(e.NewElement.MaxProgress);
+
+ _animationView.AnimationSpeed = e.NewElement.Speed;
+ _animationView.LoopAnimation = e.NewElement.RepeatMode == RepeatMode.Infinite;
+ //_animationView.RepeatCount = e.NewElement.RepeatCount;
+ //if (!string.IsNullOrEmpty(e.NewElement.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = e.NewElement.ImageAssetsFolder;
+
+ //TODO: see if this needs to be enabled
+ //_animationView.ContentScaleFactor = Convert.ToSingle(e.NewElement.Scale);
+
+ //_animationView.Frame = e.NewElement.Frame;
+ _animationView.AnimationProgress = e.NewElement.Progress;
+
+ _gestureRecognizer = new UITapGestureRecognizer(e.NewElement.InvokeClick);
+ _animationView.AddGestureRecognizer(_gestureRecognizer);
+
+ SetNativeControl(_animationView);
+ SetNeedsLayout();
+
+ if (e.NewElement.AutoPlay || e.NewElement.IsAnimating)
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+
+ //e.NewElement.Duration = TimeSpan.FromMilliseconds(_animationView.AnimationDuration);
+ }
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ //CleanupResources();
+ var composition = Element.GetAnimation();
+ _animationView.SceneModel = composition;
+ Element.InvokeAnimationLoaded(composition);
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+
+ if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ _animationView.CacheEnable = Element.CacheComposition;
+
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ //if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ // _animationView.SetMinFrame(Element.MinFrame);
+
+ //if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ // _animationView.SetMinProgress(Element.MinProgress);
+
+ //if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ // _animationView.SetMaxFrame(Element.MaxFrame);
+
+ //if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ // _animationView.SetMaxProgress(Element.MaxProgress);
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.AnimationSpeed = Element.Speed;
+
+ if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName)
+ _animationView.LoopAnimation = Element.RepeatMode == RepeatMode.Infinite;
+
+ //if (e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ // _animationView.RepeatCount = Element.RepeatCount;
+
+ //if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ //if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ // _animationView.Scale = Element.Scale;
+
+ //if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ // _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.AnimationProgress = Element.Progress;
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ private void AnimationCompletionBlock(bool animationFinished)
+ {
+ if (animationFinished)
+ {
+ if (_animationView == null || Element == null)
+ return;
+
+ Element.InvokeFinishedAnimation();
+
+ // Can be null depending if the user callback is executed very quickly
+ // and disposes the Xamarin.Forms page containing the Lottie view
+ if (_animationView == null || Element == null)
+ return;
+
+ if (Element.RepeatMode == RepeatMode.Infinite)
+ {
+ Element.InvokeRepeatAnimation();
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+ else if (Element.RepeatMode == RepeatMode.Restart && repeatCount < Element.RepeatCount)
+ {
+ repeatCount++;
+ Element.InvokeRepeatAnimation();
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+ else if (Element.RepeatMode == RepeatMode.Restart && repeatCount == Element.RepeatCount)
+ {
+ repeatCount = 1;
+ }
+ }
+ }
+
+ private void CleanupResources()
+ {
+ repeatCount = 1;
+
+ if (_gestureRecognizer != null)
+ {
+ _animationView?.RemoveGestureRecognizer(_gestureRecognizer);
+ _gestureRecognizer.Dispose();
+ _gestureRecognizer = null;
+ }
+
+ if (_animationView != null)
+ {
+ _animationView.RemoveFromSuperview();
+ _animationView.Dispose();
+ _animationView = null;
+ }
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Mac/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Mac/AnimationViewExtensions.cs
new file mode 100644
index 0000000..5190233
--- /dev/null
+++ b/Lottie.Maui/Platforms/Mac/AnimationViewExtensions.cs
@@ -0,0 +1,89 @@
+using System.IO;
+using Airbnb.Lottie;
+using Foundation;
+
+namespace Lottie.Forms.Platforms.Mac
+{
+ public static class AnimationViewExtensions
+ {
+ public static LOTComposition GetAnimation(this AnimationView animationView)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ var animation = animationView.Animation;
+
+ LOTComposition composition = null;
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ if (animation is string bundleAnimation)
+ {
+ if (!string.IsNullOrEmpty(animationView.ImageAssetsFolder))
+ composition = LOTComposition.AnimationNamed(bundleAnimation, NSBundle.FromPath(animationView.ImageAssetsFolder));
+ else
+ composition = LOTComposition.AnimationNamed(bundleAnimation);
+ }
+ break;
+ case AnimationSource.Url:
+ if (animation is string stringAnimation)
+ composition = LOTComposition.AnimationNamed(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ if (animation is string jsonAnimation)
+ {
+ NSData objectData = NSData.FromString(jsonAnimation);
+ NSDictionary jsonData = (NSDictionary)NSJsonSerialization.Deserialize(objectData, NSJsonReadingOptions.MutableContainers, out _);
+ composition = LOTComposition.AnimationFromJSON(jsonData);
+ }
+ else if (animation is NSDictionary dictAnimation)
+ composition = LOTComposition.AnimationFromJSON(dictAnimation);
+ break;
+ case AnimationSource.Stream:
+ composition = animationView.GetAnimation(animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ composition = animationView.GetAnimation(animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+
+ public static LOTComposition GetAnimation(this AnimationView animationView, object animation)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ LOTComposition composition = null;
+ switch (animation)
+ {
+ case string stringAnimation:
+
+ //TODO: check if json
+ //animationView.SetAnimationFromJson(stringAnimation);
+ //TODO: check if url
+ //animationView.SetAnimationFromUrl(stringAnimation);
+
+ composition = LOTComposition.AnimationNamed(stringAnimation);
+ break;
+ case Stream streamAnimation:
+ using (StreamReader reader = new StreamReader(streamAnimation))
+ {
+ string json = reader.ReadToEnd();
+ NSData objectData = NSData.FromString(json);
+ NSDictionary jsonData = (NSDictionary)NSJsonSerialization.Deserialize(objectData, NSJsonReadingOptions.MutableContainers, out _);
+ composition = LOTComposition.AnimationFromJSON(jsonData);
+ }
+ break;
+ case null:
+ composition = null;
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Mac/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Mac/AnimationViewRenderer.cs
new file mode 100644
index 0000000..d2c6fc1
--- /dev/null
+++ b/Lottie.Maui/Platforms/Mac/AnimationViewRenderer.cs
@@ -0,0 +1,226 @@
+using System.ComponentModel;
+using Airbnb.Lottie;
+using AppKit;
+using Foundation;
+using Lottie.Forms;
+using Lottie.Forms.Platforms.Mac;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.MacOS;
+
+[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer)), Xamarin.Forms.Internals.Preserve(AllMembers = true)]
+
+namespace Lottie.Forms.Platforms.Mac
+{
+ public class AnimationViewRenderer : ViewRenderer
+ {
+ private LOTAnimationCompletionBlock _animationCompletionBlock;
+ private LOTAnimationView _animationView;
+ private NSClickGestureRecognizer _gestureRecognizer;
+ private int repeatCount = 1;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ CleanupResources();
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationCompletionBlock = new LOTAnimationCompletionBlock(AnimationCompletionBlock);
+
+ _animationView = new LOTAnimationView()
+ {
+ ContentMode = LOTViewContentMode.ScaleAspectFill,
+ Frame = Bounds,
+ AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.HeightSizable,
+ LoopAnimation = e.NewElement.RepeatMode == RepeatMode.Infinite,
+ AnimationSpeed = e.NewElement.Speed,
+ AnimationProgress = e.NewElement.Progress,
+ CacheEnable = e.NewElement.CacheComposition,
+ CompletionBlock = _animationCompletionBlock
+ };
+
+ var composition = e.NewElement.GetAnimation();
+ _animationView.SceneModel = composition;
+ e.NewElement.InvokeAnimationLoaded(composition);
+
+ e.NewElement.PlayCommand = new Command(() =>
+ {
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ e.NewElement.InvokePlayAnimation();
+ });
+ e.NewElement.PauseCommand = new Command(() =>
+ {
+ _animationView.Pause();
+ e.NewElement.InvokePauseAnimation();
+ });
+ e.NewElement.ResumeCommand = new Command(() =>
+ {
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ e.NewElement.InvokeResumeAnimation();
+ });
+ e.NewElement.StopCommand = new Command(() =>
+ {
+ _animationView.Stop();
+ e.NewElement.InvokeStopAnimation();
+ });
+ e.NewElement.ClickCommand = new Command(() =>
+ {
+ //_animationView.Click();
+ //e.NewElement.InvokeClick();
+ });
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ _animationView.PlayFromFrame(NSNumber.FromInt32(minFrame), NSNumber.FromInt32(maxFrame), AnimationCompletionBlock);
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ _animationView.PlayFromProgress(minProgress, maxProgress, AnimationCompletionBlock);
+ });
+ e.NewElement.ReverseAnimationSpeedCommand = new Command(() => _animationView.AutoReverseAnimation = !_animationView.AutoReverseAnimation);
+
+ _animationView.CacheEnable = e.NewElement.CacheComposition;
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ //TODO: makes animation stop with current default values
+ //_animationView.SetMinFrame(e.NewElement.MinFrame);
+ //_animationView.SetMinProgress(e.NewElement.MinProgress);
+ //_animationView.SetMaxFrame(e.NewElement.MaxFrame);
+ //_animationView.SetMaxProgress(e.NewElement.MaxProgress);
+
+ _animationView.AnimationSpeed = e.NewElement.Speed;
+ _animationView.LoopAnimation = e.NewElement.RepeatMode == RepeatMode.Infinite;
+ //_animationView.RepeatCount = e.NewElement.RepeatCount;
+ //if (!string.IsNullOrEmpty(e.NewElement.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = e.NewElement.ImageAssetsFolder;
+ //_animationView.ContentScaleFactor = e.NewElement.Scale;
+ //_animationView.Frame = e.NewElement.Frame;
+ _animationView.AnimationProgress = e.NewElement.Progress;
+
+ _gestureRecognizer = new NSClickGestureRecognizer(e.NewElement.InvokeClick);
+ _animationView.AddGestureRecognizer(_gestureRecognizer);
+
+ SetNativeControl(_animationView);
+ //SetNeedsLayout();
+
+ if (e.NewElement.AutoPlay || e.NewElement.IsAnimating)
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+
+ //e.NewElement.Duration = TimeSpan.FromMilliseconds(_animationView.AnimationDuration);
+ }
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ //CleanupResources();
+ var composition = Element.GetAnimation();
+ _animationView.SceneModel = composition;
+ Element.InvokeAnimationLoaded(composition);
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+
+ if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ _animationView.CacheEnable = Element.CacheComposition;
+
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ //if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ // _animationView.SetMinFrame(Element.MinFrame);
+
+ //if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ // _animationView.SetMinProgress(Element.MinProgress);
+
+ //if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ // _animationView.SetMaxFrame(Element.MaxFrame);
+
+ //if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ // _animationView.SetMaxProgress(Element.MaxProgress);
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.AnimationSpeed = Element.Speed;
+
+ if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName)
+ _animationView.LoopAnimation = Element.RepeatMode == RepeatMode.Infinite;
+
+ //if (e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ // _animationView.RepeatCount = Element.RepeatCount;
+
+ //if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ //if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ // _animationView.Scale = Element.Scale;
+
+ //if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ // _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.AnimationProgress = Element.Progress;
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ private void AnimationCompletionBlock(bool animationFinished)
+ {
+ if (animationFinished)
+ {
+ Element?.InvokeFinishedAnimation();
+ if (Element.RepeatMode == RepeatMode.Infinite)
+ {
+ Element.InvokeRepeatAnimation();
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+ else if (Element.RepeatMode == RepeatMode.Restart && repeatCount < Element.RepeatCount)
+ {
+ repeatCount++;
+ Element.InvokeRepeatAnimation();
+ _animationView.PlayWithCompletion(AnimationCompletionBlock);
+ }
+ else if (Element.RepeatMode == RepeatMode.Restart && repeatCount == Element.RepeatCount)
+ {
+ repeatCount = 1;
+ }
+ }
+ }
+
+ private void CleanupResources()
+ {
+ repeatCount = 1;
+
+ if (_gestureRecognizer != null)
+ {
+ _animationView?.RemoveGestureRecognizer(_gestureRecognizer);
+ _gestureRecognizer.Dispose();
+ _gestureRecognizer = null;
+ }
+
+ if (_animationView != null)
+ {
+ _animationView.RemoveFromSuperview();
+ _animationView.Dispose();
+ _animationView = null;
+ }
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Tizen/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Tizen/AnimationViewExtensions.cs
new file mode 100644
index 0000000..8f96cf0
--- /dev/null
+++ b/Lottie.Maui/Platforms/Tizen/AnimationViewExtensions.cs
@@ -0,0 +1,74 @@
+using System.IO;
+using ElottieSharp;
+using Xamarin.Forms.Platform.Tizen;
+
+namespace Lottie.Forms.Platforms.Tizen
+{
+ public static class AnimationViewExtensions
+ {
+ public static void TrySetAnimation(this LottieAnimationView lottieAnimationView, AnimationView animationView)
+ {
+ if (lottieAnimationView == null)
+ throw new ArgumentNullException(nameof(lottieAnimationView));
+
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ lottieAnimationView.TrySetAnimation(animationView, ResourcePath.GetPath(animationView.Animation as string));
+ break;
+ case AnimationSource.Url:
+ if (animationView.Animation is string stringAnimation)
+ lottieAnimationView.SetAnimation(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ if (animationView.Animation is string jsonAnimation)
+ lottieAnimationView.SetAnimation(jsonAnimation);
+ break;
+ case AnimationSource.Stream:
+ lottieAnimationView.TrySetAnimation(animationView, animationView.Animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ lottieAnimationView.TrySetAnimation(animationView, animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ }
+
+ public static void TrySetAnimation(this LottieAnimationView lottieAnimationView, AnimationView animationView, object animation)
+ {
+ if (lottieAnimationView == null)
+ throw new ArgumentNullException(nameof(lottieAnimationView));
+
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ switch (animation)
+ {
+ case int intAnimation:
+ //lottieAnimationView.SetAnimation(intAnimation);
+ break;
+ case string stringAnimation:
+
+ //TODO: check if json
+ //animationView.SetAnimationFromJson(stringAnimation);
+ //TODO: check if url
+ //animationView.SetAnimationFromUrl(stringAnimation);
+
+ lottieAnimationView.SetAnimation(stringAnimation);
+ break;
+ case Stream streamAnimation:
+ //lottieAnimationView.SetAnimation(streamAnimation, null);
+ break;
+ case null:
+ lottieAnimationView.Stop();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Tizen/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Tizen/AnimationViewRenderer.cs
new file mode 100644
index 0000000..631f3c2
--- /dev/null
+++ b/Lottie.Maui/Platforms/Tizen/AnimationViewRenderer.cs
@@ -0,0 +1,217 @@
+using System.ComponentModel;
+using ElottieSharp;
+using Lottie.Forms;
+using Lottie.Forms.Platforms.Tizen;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer)), Xamarin.Forms.Internals.Preserve(AllMembers = true)]
+namespace Lottie.Forms.Platforms.Tizen
+{
+ public class AnimationViewRenderer : ViewRenderer
+ {
+ private LottieAnimationView _animationView;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ _animationView.Finished -= _animationView_Finished;
+ _animationView.Paused -= _animationView_Paused;
+ _animationView.Shown -= _animationView_Shown;
+ _animationView.Started -= _animationView_Started;
+ _animationView.Stopped -= _animationView_Stopped;
+ _animationView.FrameUpdated -= _animationView_FrameUpdated;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationView = new LottieAnimationView(Xamarin.Forms.Forms.NativeParent);
+ _animationView.Finished += _animationView_Finished;
+ _animationView.Paused += _animationView_Paused;
+ _animationView.Shown += _animationView_Shown;
+ _animationView.Started += _animationView_Started;
+ _animationView.Stopped += _animationView_Stopped;
+ _animationView.FrameUpdated += _animationView_FrameUpdated;
+
+ /*
+ _clickListener = new ClickListener
+ {
+ OnClickImpl = () => e.NewElement.InvokeClick()
+ };*/
+
+ _animationView.TrySetAnimation(e.NewElement);
+
+ e.NewElement.PlayCommand = new Command(() => _animationView.Play());
+ e.NewElement.PauseCommand = new Command(() => _animationView.Pause());
+ e.NewElement.ResumeCommand = new Command(() => _animationView.Play());
+ e.NewElement.StopCommand = new Command(() => _animationView.Stop());
+ //e.NewElement.ClickCommand = new Command(() => _animationView.PerformClick());
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ {
+ _animationView.Play(minFrame, maxFrame);
+ }
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ {
+ _animationView.Play(minProgress, maxProgress);
+ }
+ });
+ //e.NewElement.ReverseAnimationSpeedCommand = new Command(() => _animationView.ReverseAnimationSpeed());
+
+ //_animationView.SetCacheComposition(e.NewElement.CacheComposition);
+ //_animationView.SetFallbackResource(e.NewElement.FallbackResource.);
+ //_animationView.Composition = e.NewElement.Composition;
+
+ //if (e.NewElement.MinFrame != int.MinValue)
+ // _animationView.SetMinFrame(e.NewElement.MinFrame);
+ if (e.NewElement.MinProgress != float.MinValue)
+ _animationView.MinimumProgress = e.NewElement.MinProgress;
+ //if (e.NewElement.MaxFrame != int.MinValue)
+ // _animationView.SetMaxFrame(e.NewElement.MaxFrame);
+ if (e.NewElement.MaxProgress != float.MinValue)
+ _animationView.MaximumProgress = e.NewElement.MaxProgress;
+
+ _animationView.Speed = e.NewElement.Speed;
+ _animationView.AutoRepeat = e.NewElement.RepeatMode == RepeatMode.Infinite;
+ //_animationView.RepeatCount = e.NewElement.RepeatCount;
+
+ //if (!string.IsNullOrEmpty(e.NewElement.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = e.NewElement.ImageAssetsFolder;
+ //_animationView.Scale = e.NewElement.Scale;
+ //_animationView.Frame = e.NewElement.Frame;
+ _animationView.SeekTo(Element.Progress);
+
+ SetNativeControl(_animationView);
+
+ if (e.NewElement.AutoPlay || e.NewElement.IsAnimating)
+ _animationView.Play();
+
+ //e.NewElement.Duration = _animationView.DurationTime;
+ e.NewElement.IsAnimating = _animationView.IsPlaying;
+ }
+ }
+ }
+
+ private void _animationView_FrameUpdated(object sender, FrameEventArgs e)
+ {
+ Element?.InvokeAnimationUpdate(e.CurrentFrame);
+ }
+
+ private void _animationView_Stopped(object sender, System.EventArgs e)
+ {
+ Element?.InvokeStopAnimation();
+ }
+
+ private void _animationView_Started(object sender, System.EventArgs e)
+ {
+ Element?.InvokePlayAnimation();
+ }
+
+ private void _animationView_Shown(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void _animationView_Paused(object sender, System.EventArgs e)
+ {
+ Element?.InvokePauseAnimation();
+ }
+
+ private void _animationView_Finished(object sender, System.EventArgs e)
+ {
+ Element?.InvokeFinishedAnimation();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ _animationView.TrySetAnimation(Element);
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ _animationView.Play();
+ }
+
+ //if (e.PropertyName == AnimationView.AutoPlayProperty.PropertyName)
+ // _animationView.AutoPlay = (Element.AutoPlay);
+
+ //if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ // _animationView.SetCacheComposition(Element.CacheComposition);
+
+ //if (e.PropertyName == AnimationView.FallbackResource.PropertyName)
+ // _animationView.SetFallbackResource(e.NewElement.FallbackResource);
+
+ //if (e.PropertyName == AnimationView.Composition.PropertyName)
+ // _animationView.Composition = e.NewElement.Composition;
+
+ //if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ // _animationView.SetMinFrame(Element.MinFrame);
+
+ if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ _animationView.MinimumProgress = Element.MinProgress;
+
+ //if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ // _animationView.SetMaxFrame(Element.MaxFrame);
+
+ if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ _animationView.MaximumProgress = Element.MaxProgress;
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.Speed = (double)new decimal(Element.Speed);
+
+ if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName)
+ _animationView.AutoRepeat = Element.RepeatMode == RepeatMode.Infinite;
+
+ //if (e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ // _animationView.RepeatCount = Element.RepeatCount;
+
+ //if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ //if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ // _animationView.Scale = Element.Scale;
+
+ //if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ // _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.SeekTo(Element.Progress);
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_animationView != null)
+ {
+ _animationView.Finished -= _animationView_Finished;
+ _animationView.Paused -= _animationView_Paused;
+ _animationView.Shown -= _animationView_Shown;
+ _animationView.Started -= _animationView_Started;
+ _animationView.Stopped -= _animationView_Stopped;
+ _animationView.FrameUpdated -= _animationView_FrameUpdated;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Uap/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Uap/AnimationViewExtensions.cs
new file mode 100644
index 0000000..62f6f01
--- /dev/null
+++ b/Lottie.Maui/Platforms/Uap/AnimationViewExtensions.cs
@@ -0,0 +1,76 @@
+using System.IO;
+using Microsoft.Toolkit.Uwp.UI.Lottie;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Lottie.Forms.Platforms.Uap
+{
+ public static class AnimationViewExtensions
+ {
+ public static async Task GetAnimationAsync(this AnimationView animationView)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ IAnimatedVisualSource animatedVisualSource = null;
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ if (animationView.Animation is string assetAnnimation)
+ {
+ var assets = "Assets";
+
+ if (!string.IsNullOrEmpty(animationView.ImageAssetsFolder))
+ {
+ assets = animationView.ImageAssetsFolder;
+ }
+
+ var path = $"ms-appx:///{assets}/{assetAnnimation}";
+ animatedVisualSource = await animationView.GetAnimationAsync(path);
+ }
+ break;
+ case AnimationSource.Url:
+ if (animationView.Animation is string stringAnimation)
+ animatedVisualSource = await animationView.GetAnimationAsync(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ //if (animation is string jsonAnimation)
+ // animatedVisualSource = LottieVisualSource.CreateFromString(jsonAnimation);
+ break;
+ case AnimationSource.Stream:
+ animatedVisualSource = await animationView.GetAnimationAsync(animationView.Animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ animatedVisualSource = await animationView.GetAnimationAsync(animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ return animatedVisualSource;
+ }
+
+ public static async Task GetAnimationAsync(this AnimationView animationView, object animation)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ IAnimatedVisualSource animatedVisualSource = null;
+ switch (animation)
+ {
+ case string stringAnimation:
+ animatedVisualSource = LottieVisualSource.CreateFromString(stringAnimation);
+ break;
+ case Stream streamAnimation:
+ var source = new LottieVisualSource();
+ await source.SetSourceAsync(streamAnimation.AsInputStream());
+ animatedVisualSource = source;
+ break;
+ case null:
+ animatedVisualSource = null;
+ break;
+ default:
+ break;
+ }
+ return animatedVisualSource;
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Uap/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Uap/AnimationViewRenderer.cs
new file mode 100644
index 0000000..a1ec423
--- /dev/null
+++ b/Lottie.Maui/Platforms/Uap/AnimationViewRenderer.cs
@@ -0,0 +1,272 @@
+using System.ComponentModel;
+using Lottie.Forms;
+using Lottie.Forms.Platforms.Uap;
+using Microsoft.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.UWP;
+
+[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer)), Xamarin.Forms.Internals.Preserve(AllMembers = true)]
+
+namespace Lottie.Forms.Platforms.Uap
+{
+ public class AnimationViewRenderer : ViewRenderer
+ {
+ private AnimatedVisualPlayer _animationView;
+ //private bool _needToReverseAnimationSpeed;
+ //private bool _needToResetFrames;
+
+ public static int PlayDelay { get; set; } = 1000;
+
+ protected override async void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ _animationView.Loaded -= _animationView_Loaded;
+ _animationView.Tapped -= _animationView_Tapped;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationView = new AnimatedVisualPlayer
+ {
+ AutoPlay = false,
+ PlaybackRate = e.NewElement.Speed,
+ //Scale = new System.Numerics.Vector3(e.NewElement.Scale)
+ };
+ _animationView.Loaded += _animationView_Loaded;
+ _animationView.Tapped += _animationView_Tapped;
+
+ var composition = await e.NewElement.GetAnimationAsync();
+ _animationView.Source = composition;
+ e.NewElement.InvokeAnimationLoaded(composition);
+
+ e.NewElement.PlayCommand = new Command(() =>
+ {
+ _animationView.PlayAsync(0, 1, Element.RepeatMode == RepeatMode.Infinite).AsTask();
+ e.NewElement.InvokePlayAnimation();
+ });
+ e.NewElement.PauseCommand = new Command(() =>
+ {
+ _animationView.Pause();
+ e.NewElement.InvokePauseAnimation();
+ });
+ e.NewElement.ResumeCommand = new Command(() =>
+ {
+ _animationView.Resume();
+ e.NewElement.InvokeResumeAnimation();
+ });
+ e.NewElement.StopCommand = new Command(() =>
+ {
+ _animationView.Stop();
+ e.NewElement.InvokeStopAnimation();
+ });
+ e.NewElement.ClickCommand = new Command(() =>
+ {
+ //_animationView.Click();
+ //e.NewElement.InvokeClick();
+ });
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ {
+ _ = _animationView.PlayAsync(minFrame, maxFrame, Element.RepeatMode == RepeatMode.Infinite).AsTask();
+ }
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ {
+ _ = _animationView.PlayAsync(minProgress, maxProgress, Element.RepeatMode == RepeatMode.Infinite).AsTask();
+ }
+ });
+ e.NewElement.ReverseAnimationSpeedCommand = new Command(() =>
+ {
+ _animationView.PlaybackRate = -1;
+ });
+
+ e.NewElement.Duration = _animationView.Duration.Ticks;
+ e.NewElement.IsAnimating = _animationView.IsPlaying;
+
+ SetNativeControl(_animationView);
+ }
+ }
+ }
+
+ private void _animationView_Tapped(object sender, TappedRoutedEventArgs e)
+ {
+ Element?.InvokeClick();
+ }
+
+ private async void _animationView_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ {
+ await Task.Delay(PlayDelay);
+ _ = _animationView.PlayAsync(0, 1, Element.RepeatMode == RepeatMode.Infinite).AsTask();
+
+ Element.IsAnimating = _animationView.IsPlaying;
+ Element.InvokePlayAnimation();
+ }
+ }
+
+ protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ var composition = await Element.GetAnimationAsync();
+ _animationView.Source = composition;
+ Element.InvokeAnimationLoaded(composition);
+ }
+
+ if (e.PropertyName == AnimationView.AutoPlayProperty.PropertyName)
+ _animationView.AutoPlay = Element.AutoPlay;
+
+ //if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ // _animationView.SetCacheComposition(Element.CacheComposition);
+
+ //if (e.PropertyName == AnimationView.FallbackResource.PropertyName)
+ // _animationView.SetFallbackResource(e.NewElement.FallbackResource);
+
+ //if (e.PropertyName == AnimationView.Composition.PropertyName)
+ // _animationView.Composition = e.NewElement.Composition;
+
+ //if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ // _animationView.SetMinFrame(Element.MinFrame);
+
+ //if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ // _animationView.SetMinProgress(Element.MinProgress);
+
+ //if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ // _animationView.SetMaxFrame(Element.MaxFrame);
+
+ //if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ // _animationView.SetMaxProgress(Element.MaxProgress);
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.PlaybackRate = Element.Speed;
+
+ //if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName)
+ // _animationView.RepeatMode = (int)Element.RepeatMode;
+
+ //if (e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ // _animationView.RepeatCount = Element.RepeatCount;
+
+ //if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ //if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ // _animationView.Scale = Element.Scale;
+
+ //if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ // _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.SetProgress(Element.Progress);
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ /*
+
+ private void PrepareReverseAnimation(Action action, float from, float to)
+ {
+ var minValue = Math.Min(from, to);
+ var maxValue = Math.Max(from, to);
+ var needReverse = from > to;
+
+ action(minValue, maxValue);
+
+ if (needReverse && !_needToReverseAnimationSpeed)
+ {
+ _needToReverseAnimationSpeed = true;
+ _animationView.PlaybackRate = -1;
+ }
+
+ Play(minValue, maxValue);
+ }
+
+ private void OnPlayProgressSegment(object sender, ProgressSegmentEventArgs e)
+ {
+ if (_animationView != null && Element != null)
+ {
+ PrepareReverseAnimation((min, max) => Play(min, max), e.From, e.To);
+ }
+ }
+
+ private void OnPlayFrameSegment(object sender, FrameSegmentEventArgs e)
+ {
+ if (_animationView != null && Element != null)
+ {
+ PrepareReverseAnimation((min, max) =>
+ {
+ Play(min, max);
+ _needToResetFrames = true;
+ }, e.From, e.To);
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (_animationView == null || Element == null)
+ {
+
+ return;
+ }
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ if (string.IsNullOrEmpty(Element.Animation))
+ {
+ _animationView.Stop();
+ RestAnimation();
+ Element.Duration = TimeSpan.Zero;
+ Element.IsPlaying = false;
+ return;
+ }
+
+ SetAnimation(Element.Animation);
+ Element.Duration = _animationView.Duration;
+
+ #pragma warning disable CS0618 // Type or member is obsolete
+ if (Element.AutoPlay || Element.IsPlaying)
+ #pragma warning restore CS0618 // Type or member is obsolete
+ {
+ Play();
+ }
+ else
+ {
+ _animationView.Stop();
+ }
+ }
+ else if (e.PropertyName == AnimationView.IsPlayingProperty.PropertyName && !string.IsNullOrEmpty(Element.Animation))
+ {
+ if (Element?.IsPlaying == true)
+ {
+ Play();
+ }
+ else
+ {
+ _animationView?.Pause();
+ }
+ }
+ }
+ */
+ }
+}
diff --git a/Lottie.Maui/Platforms/Wpf/AnimationViewExtensions.cs b/Lottie.Maui/Platforms/Wpf/AnimationViewExtensions.cs
new file mode 100644
index 0000000..843fe57
--- /dev/null
+++ b/Lottie.Maui/Platforms/Wpf/AnimationViewExtensions.cs
@@ -0,0 +1,76 @@
+using System.IO;
+using LottieSharp;
+
+namespace Lottie.Forms.Platforms.Wpf
+{
+ public static class AnimationViewExtensions
+ {
+ public static LottieComposition GetAnimation(this AnimationView animationView)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ LottieComposition composition = null;
+ switch (animationView.AnimationSource)
+ {
+ case AnimationSource.AssetOrBundle:
+ if (animationView.Animation is string assetAnnimation)
+ {
+ var assets = "Assets";
+
+ if (!string.IsNullOrEmpty(animationView.ImageAssetsFolder))
+ {
+ assets = animationView.ImageAssetsFolder;
+ }
+
+ var path = $"ms-appx:///{assets}/{assetAnnimation}";
+ composition = animationView.GetAnimation(path);
+ }
+ break;
+ case AnimationSource.Url:
+ if (animationView.Animation is string stringAnimation)
+ composition = animationView.GetAnimation(stringAnimation);
+ break;
+ case AnimationSource.Json:
+ //if (animation is string jsonAnimation)
+ // animatedVisualSource = LottieVisualSource.CreateFromString(jsonAnimation);
+ break;
+ case AnimationSource.Stream:
+ composition = animationView.GetAnimation(animationView.Animation);
+ break;
+ case AnimationSource.EmbeddedResource:
+ composition = animationView.GetAnimation(animationView.GetStreamFromAssembly());
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+
+ public static LottieComposition GetAnimation(this AnimationView animationView, object animation)
+ {
+ if (animationView == null)
+ throw new ArgumentNullException(nameof(animationView));
+
+ LottieComposition composition = null;
+ switch (animation)
+ {
+ case string stringAnimation:
+ composition = LottieCompositionFactory.FromJsonStringSync(stringAnimation, null).Value;
+ break;
+ case Stream streamAnimation:
+ //TODO: api for this will be added in next Lottie UWP update
+ //var source = new LottieVisualSource();
+ //source.SetSourceAsync(streamAnimation);
+ //animatedVisualSource = source;
+ break;
+ case null:
+ composition = null;
+ break;
+ default:
+ break;
+ }
+ return composition;
+ }
+ }
+}
diff --git a/Lottie.Maui/Platforms/Wpf/AnimationViewRenderer.cs b/Lottie.Maui/Platforms/Wpf/AnimationViewRenderer.cs
new file mode 100644
index 0000000..0c0d7b5
--- /dev/null
+++ b/Lottie.Maui/Platforms/Wpf/AnimationViewRenderer.cs
@@ -0,0 +1,191 @@
+using System.ComponentModel;
+using Lottie.Forms;
+using Lottie.Forms.Platforms.Wpf;
+using LottieSharp;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Platform.WPF;
+
+[assembly: ExportRenderer(typeof(AnimationView), typeof(AnimationViewRenderer)), Preserve(AllMembers = true)]
+
+namespace Lottie.Forms.Platforms.Wpf
+{
+ public class AnimationViewRenderer : ViewRenderer
+ {
+ private LottieAnimationView _animationView;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e == null)
+ return;
+
+ if (e.OldElement != null)
+ {
+ _animationView.Loaded -= _animationView_Loaded;
+ _animationView.MouseDown -= _animationView_MouseDown;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ _animationView = new LottieAnimationView
+ {
+ AutoPlay = false,
+ //PlaybackRate = e.NewElement.Speed,
+ //Scale = new System.Numerics.Vector3(e.NewElement.Scale)
+ };
+ _animationView.Loaded += _animationView_Loaded;
+ _animationView.MouseDown += _animationView_MouseDown;
+
+ //_animationView.FileName = e.NewElement.Animation as string;
+ var composition = e.NewElement.GetAnimation();
+ _animationView.Composition = composition;
+ e.NewElement.InvokeAnimationLoaded(composition);
+
+ e.NewElement.PlayCommand = new Command(() =>
+ {
+ _animationView.PlayAnimation();
+ e.NewElement.InvokePlayAnimation();
+ });
+ e.NewElement.PauseCommand = new Command(() =>
+ {
+ _animationView.PauseAnimation();
+ e.NewElement.InvokePauseAnimation();
+ });
+ e.NewElement.ResumeCommand = new Command(() =>
+ {
+ _animationView.ResumeAnimation();
+ e.NewElement.InvokeResumeAnimation();
+ });
+ e.NewElement.StopCommand = new Command(() =>
+ {
+ _animationView.CancelAnimation();
+ e.NewElement.InvokeStopAnimation();
+ });
+ e.NewElement.ClickCommand = new Command(() =>
+ {
+ //_animationView.Click();
+ //e.NewElement.InvokeClick();
+ });
+
+ e.NewElement.PlayMinAndMaxFrameCommand = new Command((object paramter) =>
+ {
+ if (paramter is (int minFrame, int maxFrame))
+ {
+ _animationView.SetMinAndMaxFrame(minFrame, maxFrame);
+ _animationView.PlayAnimation();
+ }
+ });
+ e.NewElement.PlayMinAndMaxProgressCommand = new Command((object paramter) =>
+ {
+ if (paramter is (float minProgress, float maxProgress))
+ {
+ _animationView.SetMinAndMaxProgress(minProgress, maxProgress);
+ _animationView.PlayAnimation();
+ }
+ });
+ e.NewElement.ReverseAnimationSpeedCommand = new Command(() =>
+ {
+ _animationView.ReverseAnimationSpeed();
+ });
+
+ //e.NewElement.Duration = _animationView.Duration.Ticks;
+ //e.NewElement.IsAnimating = _animationView.IsPlaying;
+
+ SetNativeControl(_animationView);
+
+ if (e.NewElement.AutoPlay || e.NewElement.IsAnimating)
+ _animationView.PlayAnimation();
+ }
+ }
+ }
+
+ private void _animationView_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+ {
+ Element?.InvokeClick();
+ }
+
+ private void _animationView_Loaded(object sender, System.Windows.RoutedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ {
+ _animationView.PlayAnimation();
+
+ //await Task.Delay(PlayDelay);
+ //_ = _animationView.PlayAsync(0, 1, Element.RepeatMode == RepeatMode.Infinite).AsTask();
+
+ //Element.IsAnimating = _animationView.IsPlaying;
+ Element.InvokePlayAnimation();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_animationView == null || Element == null || e == null)
+ return;
+
+ if (e.PropertyName == AnimationView.AnimationProperty.PropertyName)
+ {
+ var composition = Element.GetAnimation();
+ _animationView.Composition = composition;
+ Element.InvokeAnimationLoaded(composition);
+
+ if (Element.AutoPlay || Element.IsAnimating)
+ _animationView.PlayAnimation();
+ }
+
+ if (e.PropertyName == AnimationView.AutoPlayProperty.PropertyName)
+ _animationView.AutoPlay = Element.AutoPlay;
+
+ //if (e.PropertyName == AnimationView.CacheCompositionProperty.PropertyName)
+ // _animationView.DefaultCacheStrategy = LottieAnimationView.CacheStrategy.Strong (Element.CacheComposition);
+
+ //if (e.PropertyName == AnimationView.FallbackResource.PropertyName)
+ // _animationView.SetFallbackResource(e.NewElement.FallbackResource);
+
+ //if (e.PropertyName == AnimationView.Composition.PropertyName)
+ // _animationView.Composition = e.NewElement.Composition;
+
+ if (e.PropertyName == AnimationView.MinFrameProperty.PropertyName)
+ _animationView.MinFrame = Element.MinFrame;
+
+ if (e.PropertyName == AnimationView.MinProgressProperty.PropertyName)
+ _animationView.MinProgress = Element.MinProgress;
+
+ if (e.PropertyName == AnimationView.MaxFrameProperty.PropertyName)
+ _animationView.MaxFrame = Element.MaxFrame;
+
+ if (e.PropertyName == AnimationView.MaxProgressProperty.PropertyName)
+ _animationView.MaxProgress = Element.MaxProgress;
+
+ if (e.PropertyName == AnimationView.SpeedProperty.PropertyName)
+ _animationView.Speed = Element.Speed;
+
+ if (e.PropertyName == AnimationView.RepeatModeProperty.PropertyName)
+ _animationView.RepeatMode = (LottieSharp.RepeatMode)(int)Element.RepeatMode;
+
+ if (e.PropertyName == AnimationView.RepeatCountProperty.PropertyName)
+ _animationView.RepeatCount = Element.RepeatCount;
+
+ //if (e.PropertyName == AnimationView.ImageAssetsFolderProperty.PropertyName && !string.IsNullOrEmpty(Element.ImageAssetsFolder))
+ // _animationView.ImageAssetsFolder = Element.ImageAssetsFolder;
+
+ if (e.PropertyName == AnimationView.ScaleProperty.PropertyName)
+ _animationView.Scale = Element.Scale;
+
+ if (e.PropertyName == AnimationView.FrameProperty.PropertyName)
+ _animationView.Frame = Element.Frame;
+
+ if (e.PropertyName == AnimationView.ProgressProperty.PropertyName)
+ _animationView.Progress = Element.Progress;
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+ }
+}
diff --git a/Lottie.Maui/RenderMode.cs b/Lottie.Maui/RenderMode.cs
new file mode 100644
index 0000000..fce9dbe
--- /dev/null
+++ b/Lottie.Maui/RenderMode.cs
@@ -0,0 +1,9 @@
+namespace Lottie.Maui
+{
+ public enum RenderMode
+ {
+ Automatic,
+ Hardware,
+ Software
+ }
+}
diff --git a/Lottie.Maui/RepeatMode.cs b/Lottie.Maui/RepeatMode.cs
new file mode 100644
index 0000000..bcc2516
--- /dev/null
+++ b/Lottie.Maui/RepeatMode.cs
@@ -0,0 +1,20 @@
+namespace Lottie.Maui
+{
+ public enum RepeatMode
+ {
+ ///
+ /// When the animation reaches the end and RepeatCount is Infinite or a positive value, the animation restarts from the beginning.
+ ///
+ Restart = 0,
+
+ ///
+ /// When the animation reaches the end and RepeatCount is Infinite or a positive value, the animation reverses direction on every iteration.
+ ///
+ Reverse = 1,
+
+ ///
+ /// Repeat the animation indefinitely.
+ ///
+ Infinite = 2
+ }
+}
diff --git a/Lottie.Maui/readme.txt b/Lottie.Maui/readme.txt
new file mode 100644
index 0000000..cc3fffe
--- /dev/null
+++ b/Lottie.Maui/readme.txt
@@ -0,0 +1,66 @@
+---------------------------------
+Lottie
+---------------------------------
+
+Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as json with Bodymovin and renders them natively on mobile!
+
+Using Lottie on Xamarin.Forms:
+
+Namespace:
+
+xmlns:forms="clr-namespace:Lottie.Forms;assembly=Lottie.Forms"
+
+Example:
+
+
+
+All options:
+
+
+
+
+---------------------------------
+Star on Github if this project helps you: https://github.com/Baseflow/LottieXamarin
+
+Commercial support is available. Integration with your app or services, samples, feature request, etc. Email: hello@baseflow.com
+Powered by: https://baseflow.com
+---------------------------------
\ No newline at end of file
diff --git a/Lottie.iOS.net6/ApiDefinitions.Ios.cs b/Lottie.iOS.net6/ApiDefinitions.Ios.cs
new file mode 100644
index 0000000..a49e4d7
--- /dev/null
+++ b/Lottie.iOS.net6/ApiDefinitions.Ios.cs
@@ -0,0 +1,726 @@
+using System;
+using CoreGraphics;
+using Foundation;
+using ObjCRuntime;
+using UIKit;
+
+namespace Airbnb.Lottie
+{
+ // @interface LOTAnimationTransitionController : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTAnimationTransitionController : IUIViewControllerAnimatedTransitioning
+ {
+ // -(instancetype _Nonnull)initWithAnimationNamed:(NSString * _Nonnull)animation fromLayerNamed:(NSString * _Nullable)fromLayer toLayerNamed:(NSString * _Nullable)toLayer applyAnimationTransform:(BOOL)applyAnimationTransform;
+ [Export("initWithAnimationNamed:fromLayerNamed:toLayerNamed:applyAnimationTransform:")]
+ IntPtr Constructor(string animation, [NullAllowed] string fromLayer, [NullAllowed] string toLayer, bool applyAnimationTransform);
+
+ // -(instancetype _Nonnull)initWithAnimationNamed:(NSString * _Nonnull)animation fromLayerNamed:(NSString * _Nullable)fromLayer toLayerNamed:(NSString * _Nullable)toLayer applyAnimationTransform:(BOOL)applyAnimationTransform inBundle:(NSBundle * _Nonnull)bundle;
+ [Export("initWithAnimationNamed:fromLayerNamed:toLayerNamed:applyAnimationTransform:inBundle:")]
+ IntPtr Constructor(string animation, [NullAllowed] string fromLayer, [NullAllowed] string toLayer, bool applyAnimationTransform, NSBundle bundle);
+ }
+
+ // @interface LOTAnimatedControl : UIControl
+ [BaseType(typeof(UIControl))]
+ interface LOTAnimatedControl
+ {
+ // -(void)setLayerName:(NSString * _Nonnull)layerName forState:(UIControlState)state;
+ [Export("setLayerName:forState:")]
+ void SetLayerName(string layerName, UIControlState state);
+
+ // @property (readonly, nonatomic) LOTAnimationView * _Nonnull animationView;
+ [Export("animationView")]
+ LOTAnimationView AnimationView { get; }
+
+ // @property (nonatomic) LOTComposition * _Nullable animationComp;
+ [NullAllowed, Export("animationComp", ArgumentSemantic.Assign)]
+ LOTComposition AnimationComp { get; set; }
+ }
+
+ // @interface LOTAnimatedSwitch : LOTAnimatedControl
+ [BaseType(typeof(LOTAnimatedControl))]
+ interface LOTAnimatedSwitch
+ {
+ // +(instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName;
+ [Static]
+ [Export("switchNamed:")]
+ LOTAnimatedSwitch SwitchNamed(string toggleName);
+
+ // +(instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName inBundle:(NSBundle * _Nonnull)bundle;
+ [Static]
+ [Export("switchNamed:inBundle:")]
+ LOTAnimatedSwitch SwitchNamed(string toggleName, NSBundle bundle);
+
+ // @property (getter = isOn, nonatomic) BOOL on;
+ [Export("on")]
+ bool On { [Bind("isOn")] get; set; }
+
+ // @property (nonatomic) BOOL interactiveGesture;
+ [Export("interactiveGesture")]
+ bool InteractiveGesture { get; set; }
+
+ // -(void)setOn:(BOOL)on animated:(BOOL)animated;
+ [Export("setOn:animated:")]
+ void SetOn(bool on, bool animated);
+
+ // -(void)setProgressRangeForOnState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress;
+ [Export("setProgressRangeForOnState:toProgress:")]
+ void SetProgressRangeForOnState(nfloat fromProgress, nfloat toProgress);
+
+ // -(void)setProgressRangeForOffState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress;
+ [Export("setProgressRangeForOffState:toProgress:")]
+ void SetProgressRangeForOffState(nfloat fromProgress, nfloat toProgress);
+ }
+
+ // @interface LOTCacheProvider : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTCacheProvider
+ {
+ // +(id)imageCache;
+ // +(void)setImageCache:(id)cache;
+ [Static]
+ [Export("imageCache")]
+ LOTImageCache ImageCache { get; set; }
+ }
+
+ // @protocol LOTImageCache
+ [Protocol, Model]
+ [BaseType(typeof(NSObject))]
+ interface LOTImageCache
+ {
+ // @required -(UIImage *)imageForKey:(NSString *)key;
+ [Abstract]
+ [Export("imageForKey:")]
+ UIImage ImageForKey(string key);
+
+ // @required -(void)setImage:(UIImage *)image forKey:(NSString *)key;
+ [Abstract]
+ [Export("setImage:forKey:")]
+ void SetImage(UIImage image, string key);
+ }
+
+ // @interface LOTComposition : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTComposition
+ {
+ // +(instancetype _Nullable)animationNamed:(NSString * _Nonnull)animationName;
+ [Static]
+ [Export("animationNamed:")]
+ [return: NullAllowed]
+ LOTComposition AnimationNamed(string animationName);
+
+ // +(instancetype _Nullable)animationNamed:(NSString * _Nonnull)animationName inBundle:(NSBundle * _Nonnull)bundle;
+ [Static]
+ [Export("animationNamed:inBundle:")]
+ [return: NullAllowed]
+ LOTComposition AnimationNamed(string animationName, NSBundle bundle);
+
+ // +(instancetype _Nullable)animationWithFilePath:(NSString * _Nonnull)filePath;
+ [Static]
+ [Export("animationWithFilePath:")]
+ [return: NullAllowed]
+ LOTComposition AnimationWithFilePath(string filePath);
+
+ // +(instancetype _Nonnull)animationFromJSON:(NSDictionary * _Nonnull)animationJSON;
+ [Static]
+ [Export("animationFromJSON:")]
+ LOTComposition AnimationFromJSON(NSDictionary animationJSON);
+
+ // +(instancetype _Nonnull)animationFromJSON:(NSDictionary * _Nullable)animationJSON inBundle:(NSBundle * _Nullable)bundle;
+ [Static]
+ [Export("animationFromJSON:inBundle:")]
+ LOTComposition AnimationFromJSON([NullAllowed] NSDictionary animationJSON, [NullAllowed] NSBundle bundle);
+
+ // -(instancetype _Nonnull)initWithJSON:(NSDictionary * _Nullable)jsonDictionary withAssetBundle:(NSBundle * _Nullable)bundle;
+ [Export("initWithJSON:withAssetBundle:")]
+ IntPtr Constructor([NullAllowed] NSDictionary jsonDictionary, [NullAllowed] NSBundle bundle);
+
+ // @property (readonly, nonatomic) CGRect compBounds;
+ [Export("compBounds")]
+ CGRect CompBounds { get; }
+
+ // @property (readonly, nonatomic) NSNumber * _Nullable startFrame;
+ [NullAllowed, Export("startFrame")]
+ NSNumber StartFrame { get; }
+
+ // @property (readonly, nonatomic) NSNumber * _Nullable endFrame;
+ [NullAllowed, Export("endFrame")]
+ NSNumber EndFrame { get; }
+
+ // @property (readonly, nonatomic) NSNumber * _Nullable framerate;
+ [NullAllowed, Export("framerate")]
+ NSNumber Framerate { get; }
+
+ // @property (readonly, nonatomic) NSTimeInterval timeDuration;
+ [Export("timeDuration")]
+ double TimeDuration { get; }
+
+ // @property (readonly, nonatomic) LOTLayerGroup * _Nullable layerGroup;
+ //[NullAllowed, Export("layerGroup")]
+ //LOTLayerGroup LayerGroup { get; }
+
+ //// @property (readonly, nonatomic) LOTAssetGroup * _Nullable assetGroup;
+ //[NullAllowed, Export("assetGroup")]
+ //LOTAssetGroup AssetGroup { get; }
+
+ // @property (readwrite, nonatomic) NSString * _Nullable rootDirectory;
+ [NullAllowed, Export("rootDirectory")]
+ string RootDirectory { get; set; }
+
+ // @property (readonly, nonatomic) NSBundle * _Nullable assetBundle;
+ [NullAllowed, Export("assetBundle")]
+ NSBundle AssetBundle { get; }
+
+ // @property (copy, nonatomic) NSString * _Nullable cacheKey;
+ [NullAllowed, Export("cacheKey")]
+ string CacheKey { get; set; }
+ }
+
+
+ // @interface LOTKeypath : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTKeypath
+ {
+
+ // +(LOTKeypath * _Nonnull)keypathWithString:(NSString * _Nonnull)keypath;
+ [Static]
+ [Export("keypathWithString:")]
+ LOTKeypath KeypathWithString(string keypath);
+
+ // +(LOTKeypath * _Nonnull)keypathWithKeys:(NSString * _Nonnull)firstKey, ... __attribute__((sentinel(0, 1)));
+ [Static, Internal]
+ [Export("keypathWithKeys:", IsVariadic = true)]
+ LOTKeypath KeypathWithKeys(string firstKey, IntPtr varArgs);
+
+ // @property (readonly, nonatomic) NSString * _Nonnull absoluteKeypath;
+ [Export("absoluteKeypath")]
+ string AbsoluteKeypath { get; }
+
+ // @property (readonly, nonatomic) NSString * _Nonnull currentKey;
+ [Export("currentKey")]
+ string CurrentKey { get; }
+
+ // @property (readonly, nonatomic) NSString * _Nonnull currentKeyPath;
+ [Export("currentKeyPath")]
+ string CurrentKeyPath { get; }
+
+ // @property (readonly, nonatomic) NSDictionary * _Nonnull searchResults;
+ [Export("searchResults")]
+ NSDictionary SearchResults { get; }
+
+ // @property (readonly, nonatomic) BOOL hasFuzzyWildcard;
+ [Export("hasFuzzyWildcard")]
+ bool HasFuzzyWildcard { get; }
+
+ // @property (readonly, nonatomic) BOOL hasWildcard;
+ [Export("hasWildcard")]
+ bool HasWildcard { get; }
+
+ // @property (readonly, nonatomic) BOOL endOfKeypath;
+ [Export("endOfKeypath")]
+ bool EndOfKeypath { get; }
+
+ // -(BOOL)pushKey:(NSString * _Nonnull)key;
+ [Export("pushKey:")]
+ bool PushKey(string key);
+
+ // -(void)popKey;
+ [Export("popKey")]
+ void PopKey();
+
+ // -(void)popToRootKey;
+ [Export("popToRootKey")]
+ void PopToRootKey();
+
+ // -(void)addSearchResultForCurrentPath:(id _Nonnull)result;
+ [Export("addSearchResultForCurrentPath:")]
+ void AddSearchResultForCurrentPath(NSObject result);
+ }
+
+ // @protocol LOTValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(NSObject))]
+ interface LOTValueDelegate
+ {
+ }
+
+ // @protocol LOTColorValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(LOTValueDelegate))]
+ interface LOTColorValueDelegate
+ {
+ // @required -(CGColorRef)colorForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor currentColor:(CGColorRef)interpolatedColor;
+ [Abstract]
+ [Export("colorForFrame:startKeyframe:endKeyframe:interpolatedProgress:startColor:endColor:currentColor:")]
+ unsafe CGColor StartKeyframe(nfloat currentFrame, nfloat startKeyframe, nfloat endKeyframe, nfloat interpolatedProgress, CGColor startColor, CGColor endColor, CGColor interpolatedColor);
+ }
+
+ // @protocol LOTNumberValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(LOTValueDelegate))]
+ interface LOTNumberValueDelegate
+ {
+ // @required -(CGFloat)floatValueForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startValue:(CGFloat)startValue endValue:(CGFloat)endValue currentValue:(CGFloat)interpolatedValue;
+ [Abstract]
+ [Export("floatValueForFrame:startKeyframe:endKeyframe:interpolatedProgress:startValue:endValue:currentValue:")]
+ nfloat StartKeyframe(nfloat currentFrame, nfloat startKeyframe, nfloat endKeyframe, nfloat interpolatedProgress, nfloat startValue, nfloat endValue, nfloat interpolatedValue);
+ }
+
+ // @protocol LOTPointValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(LOTValueDelegate))]
+ interface LOTPointValueDelegate
+ {
+ // @required -(CGPoint)pointForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint currentPoint:(CGPoint)interpolatedPoint;
+ [Abstract]
+ [Export("pointForFrame:startKeyframe:endKeyframe:interpolatedProgress:startPoint:endPoint:currentPoint:")]
+ CGPoint StartKeyframe(nfloat currentFrame, nfloat startKeyframe, nfloat endKeyframe, nfloat interpolatedProgress, CGPoint startPoint, CGPoint endPoint, CGPoint interpolatedPoint);
+ }
+
+ // @protocol LOTSizeValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(LOTValueDelegate))]
+ interface LOTSizeValueDelegate
+ {
+ // @required -(CGSize)sizeForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startSize:(CGSize)startSize endSize:(CGSize)endSize currentSize:(CGSize)interpolatedSize;
+ [Abstract]
+ [Export("sizeForFrame:startKeyframe:endKeyframe:interpolatedProgress:startSize:endSize:currentSize:")]
+ CGSize StartKeyframe(nfloat currentFrame, nfloat startKeyframe, nfloat endKeyframe, nfloat interpolatedProgress, CGSize startSize, CGSize endSize, CGSize interpolatedSize);
+ }
+
+ // @protocol LOTPathValueDelegate
+ [Protocol, Model]
+ [BaseType(typeof(LOTValueDelegate))]
+ interface LOTPathValueDelegate
+ {
+ // @required -(CGPathRef)pathForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress;
+ [Abstract]
+ [Export("pathForFrame:startKeyframe:endKeyframe:interpolatedProgress:")]
+ unsafe CGPath StartKeyframe(nfloat currentFrame, nfloat startKeyframe, nfloat endKeyframe, nfloat interpolatedProgress);
+ }
+
+ // typedef void (^LOTAnimationCompletionBlock)(BOOL);
+ delegate void LOTAnimationCompletionBlock(bool animationFinished);
+
+ // @interface LOTAnimationView : UIView
+ [BaseType(typeof(UIView))]
+ interface LOTAnimationView
+ {
+ // +(instancetype _Nonnull)animationNamed:(NSString * _Nonnull)animationName;
+ [Static]
+ [Export("animationNamed:")]
+ LOTAnimationView AnimationNamed(string animationName);
+
+ // +(instancetype _Nonnull)animationNamed:(NSString * _Nonnull)animationName inBundle:(NSBundle * _Nonnull)bundle;
+ [Static]
+ [Export("animationNamed:inBundle:")]
+ LOTAnimationView AnimationNamed(string animationName, NSBundle bundle);
+
+ // +(instancetype _Nonnull)animationFromJSON:(NSDictionary * _Nonnull)animationJSON;
+ [Static]
+ [Export("animationFromJSON:")]
+ LOTAnimationView AnimationFromJSON(NSDictionary animationJSON);
+
+ // +(instancetype _Nonnull)animationWithFilePath:(NSString * _Nonnull)filePath;
+ [Static]
+ [Export("animationWithFilePath:")]
+ LOTAnimationView AnimationWithFilePath(string filePath);
+
+ // +(instancetype _Nonnull)animationFromJSON:(NSDictionary * _Nullable)animationJSON inBundle:(NSBundle * _Nullable)bundle;
+ [Static]
+ [Export("animationFromJSON:inBundle:")]
+ LOTAnimationView AnimationFromJSON([NullAllowed] NSDictionary animationJSON, [NullAllowed] NSBundle bundle);
+
+ // -(instancetype _Nonnull)initWithModel:(LOTComposition * _Nullable)model inBundle:(NSBundle * _Nullable)bundle;
+ [Export("initWithModel:inBundle:")]
+ IntPtr Constructor([NullAllowed] LOTComposition model, [NullAllowed] NSBundle bundle);
+
+ // -(instancetype _Nonnull)initWithContentsOfURL:(NSURL * _Nonnull)url;
+ [Export("initWithContentsOfURL:")]
+ IntPtr Constructor(NSUrl url);
+
+ // -(void)setAnimationNamed:(NSString * _Nonnull)animationName;
+ [Export("setAnimationNamed:")]
+ void SetAnimationNamed(string animationName);
+
+ // @property (readonly, nonatomic) BOOL isAnimationPlaying;
+ [Export("isAnimationPlaying")]
+ bool IsAnimationPlaying { get; }
+
+ // @property (assign, nonatomic) BOOL loopAnimation;
+ [Export("loopAnimation")]
+ bool LoopAnimation { get; set; }
+
+ // @property (assign, nonatomic) BOOL autoReverseAnimation;
+ [Export("autoReverseAnimation")]
+ bool AutoReverseAnimation { get; set; }
+
+ // @property (assign, nonatomic) CGFloat animationProgress;
+ [Export("animationProgress")]
+ nfloat AnimationProgress { get; set; }
+
+ // @property (assign, nonatomic) CGFloat animationSpeed;
+ [Export("animationSpeed")]
+ nfloat AnimationSpeed { get; set; }
+
+ // @property (readonly, nonatomic) CGFloat animationDuration;
+ [Export("animationDuration")]
+ nfloat AnimationDuration { get; }
+
+ // @property (assign, nonatomic) BOOL cacheEnable;
+ [Export("cacheEnable")]
+ bool CacheEnable { get; set; }
+
+ // @property (assign, nonatomic) BOOL shouldRasterizeWhenIdle;
+ [Export("shouldRasterizeWhenIdle")]
+ bool ShouldRasterizeWhenIdle { get; set; }
+
+ // @property (copy, nonatomic) LOTAnimationCompletionBlock _Nullable completionBlock;
+ [NullAllowed, Export("completionBlock", ArgumentSemantic.Copy)]
+ LOTAnimationCompletionBlock CompletionBlock { get; set; }
+
+ // @property (nonatomic, strong) LOTComposition * _Nullable sceneModel;
+ [NullAllowed, Export("sceneModel", ArgumentSemantic.Strong)]
+ LOTComposition SceneModel { get; set; }
+
+ // -(void)playToProgress:(CGFloat)toProgress withCompletion:(LOTAnimationCompletionBlock _Nullable)completion;
+ [Export("playToProgress:withCompletion:")]
+ void PlayToProgress(nfloat toProgress, [NullAllowed] LOTAnimationCompletionBlock completion);
+
+ // -(void)playFromProgress:(CGFloat)fromStartProgress toProgress:(CGFloat)toEndProgress withCompletion:(LOTAnimationCompletionBlock _Nullable)completion;
+ [Export("playFromProgress:toProgress:withCompletion:")]
+ void PlayFromProgress(nfloat fromStartProgress, nfloat toEndProgress, [NullAllowed] LOTAnimationCompletionBlock completion);
+
+ // -(void)playToFrame:(NSNumber * _Nonnull)toFrame withCompletion:(LOTAnimationCompletionBlock _Nullable)completion;
+ [Export("playToFrame:withCompletion:")]
+ void PlayToFrame(NSNumber toFrame, [NullAllowed] LOTAnimationCompletionBlock completion);
+
+ // -(void)playFromFrame:(NSNumber * _Nonnull)fromStartFrame toFrame:(NSNumber * _Nonnull)toEndFrame withCompletion:(LOTAnimationCompletionBlock _Nullable)completion;
+ [Export("playFromFrame:toFrame:withCompletion:")]
+ void PlayFromFrame(NSNumber fromStartFrame, NSNumber toEndFrame, [NullAllowed] LOTAnimationCompletionBlock completion);
+
+ // -(void)playWithCompletion:(LOTAnimationCompletionBlock _Nullable)completion;
+ [Export("playWithCompletion:")]
+ void PlayWithCompletion([NullAllowed] LOTAnimationCompletionBlock completion);
+
+ // -(void)play;
+ [Export("play")]
+ void Play();
+
+ // -(void)pause;
+ [Export("pause")]
+ void Pause();
+
+ // -(void)stop;
+ [Export("stop")]
+ void Stop();
+
+ // -(void)setProgressWithFrame:(NSNumber * _Nonnull)currentFrame;
+ [Export("setProgressWithFrame:")]
+ void SetProgressWithFrame(NSNumber currentFrame);
+
+ // -(void)forceDrawingUpdate;
+ [Export("forceDrawingUpdate")]
+ void ForceDrawingUpdate();
+
+ // -(void)logHierarchyKeypaths;
+ [Export("logHierarchyKeypaths")]
+ void LogHierarchyKeypaths();
+
+ // -(void)setValueDelegate:(id _Nonnull)delegates forKeypath:(LOTKeypath * _Nonnull)keypath;
+ [Export("setValueDelegate:forKeypath:")]
+ void SetValueDelegate(NSObject delegates, LOTKeypath keypath);
+
+ // -(NSArray * _Nullable)keysForKeyPath:(LOTKeypath * _Nonnull)keypath;
+ [Export("keysForKeyPath:")]
+ [return: NullAllowed]
+ LOTKeypath[] KeysForKeyPath(LOTKeypath keypath);
+
+ // -(CGPoint)convertPoint:(CGPoint)point toKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("convertPoint:toKeypathLayer:")]
+ CGPoint ConvertPointToKeypath(CGPoint point, LOTKeypath keypath);
+
+ // -(CGRect)convertRect:(CGRect)rect toKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("convertRect:toKeypathLayer:")]
+ CGRect ConvertRectToKeypath(CGRect rect, LOTKeypath keypath);
+
+ // -(CGPoint)convertPoint:(CGPoint)point fromKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("convertPoint:fromKeypathLayer:")]
+ CGPoint ConvertPointFromKeypath(CGPoint point, LOTKeypath keypath);
+
+ // -(CGRect)convertRect:(CGRect)rect fromKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("convertRect:fromKeypathLayer:")]
+ CGRect ConvertRectFromKeypath(CGRect rect, LOTKeypath keypath);
+
+ // -(void)addSubview:(UIView * _Nonnull)view toKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("addSubview:toKeypathLayer:")]
+ void AddSubview(UIView view, LOTKeypath keypath);
+
+ // -(void)maskSubview:(UIView * _Nonnull)view toKeypathLayer:(LOTKeypath * _Nonnull)keypath;
+ [Export("maskSubview:toKeypathLayer:")]
+ void MaskSubview(UIView view, LOTKeypath keypath);
+
+ // -(void)setValue:(id _Nonnull)value forKeypath:(NSString * _Nonnull)keypath atFrame:(NSNumber * _Nullable)frame __attribute__((deprecated("")));
+ [Export("setValue:forKeypath:atFrame:")]
+ void SetValue(NSObject value, string keypath, [NullAllowed] NSNumber frame);
+
+ // -(void)addSubview:(UIView * _Nonnull)view toLayerNamed:(NSString * _Nonnull)layer applyTransform:(BOOL)applyTransform __attribute__((deprecated("")));
+ [Export("addSubview:toLayerNamed:applyTransform:")]
+ void AddSubview(UIView view, string layer, bool applyTransform);
+
+ // -(CGRect)convertRect:(CGRect)rect toLayerNamed:(NSString * _Nullable)layerName __attribute__((deprecated("")));
+ [Export("convertRect:toLayerNamed:")]
+ CGRect ConvertRect(CGRect rect, [NullAllowed] string layerName);
+ }
+
+ // @interface LOTAnimationCache : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTAnimationCache
+ {
+ // +(instancetype _Nonnull)sharedCache;
+ [Static]
+ [Export("sharedCache")]
+ LOTAnimationCache SharedCache();
+
+ // -(void)addAnimation:(LOTComposition * _Nonnull)animation forKey:(NSString * _Nonnull)key;
+ [Export("addAnimation:forKey:")]
+ void AddAnimation(LOTComposition animation, string key);
+
+ // -(LOTComposition * _Nullable)animationForKey:(NSString * _Nonnull)key;
+ [Export("animationForKey:")]
+ [return: NullAllowed]
+ LOTComposition AnimationForKey(string key);
+
+ // -(void)removeAnimationForKey:(NSString * _Nonnull)key;
+ [Export("removeAnimationForKey:")]
+ void RemoveAnimationForKey(string key);
+
+ // -(void)clearCache;
+ [Export("clearCache")]
+ void ClearCache();
+
+ // -(void)disableCaching;
+ [Export("disableCaching")]
+ void DisableCaching();
+ }
+
+ // typedef CGColorRef _Nonnull (^LOTColorValueCallbackBlock)(CGFloat, CGFloat, CGFloat, CGFloat, CGColorRef _Nullable, CGColorRef _Nullable, CGColorRef _Nullable);
+ unsafe delegate IntPtr LOTColorValueCallbackBlock(nfloat currentFrame, nfloat startKeyFrame, nfloat endKeyFrame, nfloat interpolatedProgress, ObjCRuntime.NativeHandle startColor, ObjCRuntime.NativeHandle endColor, ObjCRuntime.NativeHandle interpolatedColor);
+
+ // typedef CGFloat (^LOTNumberValueCallbackBlock)(CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat);
+ delegate nfloat LOTNumberValueCallbackBlock(nfloat currentFrame, nfloat startKeyFrame, nfloat endKeyFrame, nfloat interpolatedProgress, nfloat startValue, nfloat endValue, nfloat interpolatedValue);
+
+ // typedef CGPoint (^LOTPointValueCallbackBlock)(CGFloat, CGFloat, CGFloat, CGFloat, CGPoint, CGPoint, CGPoint);
+ delegate CGPoint LOTPointValueCallbackBlock(nfloat currentFrame, nfloat startKeyFrame, nfloat endKeyFrame, nfloat interpolatedProgress, CGPoint startPoint, CGPoint endPoint, CGPoint interpolatedPoint);
+
+ // typedef CGSize (^LOTSizeValueCallbackBlock)(CGFloat, CGFloat, CGFloat, CGFloat, CGSize, CGSize, CGSize);
+ delegate CGSize LOTSizeValueCallbackBlock(nfloat currentFrame, nfloat startKeyFrame, nfloat endKeyFrame, nfloat interpolatedProgress, CGSize startSize, CGSize endSize, CGSize interpolatedSize);
+
+ // typedef CGPathRef _Nonnull (^LOTPathValueCallbackBlock)(CGFloat, CGFloat, CGFloat, CGFloat);
+ unsafe delegate CGPath LOTPathValueCallbackBlock(nfloat currentFrame, nfloat startKeyFrame, nfloat endKeyFrame, nfloat interpolatedProgress);
+
+ // @interface LOTColorBlockCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTColorBlockCallback : LOTColorValueDelegate
+ {
+ // +(instancetype _Nonnull)withBlock:(LOTColorValueCallbackBlock _Nonnull)block;
+ [Static]
+ [Export("withBlock:")]
+ LOTColorBlockCallback WithBlock(LOTColorValueCallbackBlock block);
+
+ // @property (copy, nonatomic) LOTColorValueCallbackBlock _Nonnull callback;
+ [Export("callback", ArgumentSemantic.Copy)]
+ LOTColorValueCallbackBlock Callback { get; set; }
+ }
+
+ // @interface LOTNumberBlockCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTNumberBlockCallback : LOTNumberValueDelegate
+ {
+ // +(instancetype _Nonnull)withBlock:(LOTNumberValueCallbackBlock _Nonnull)block;
+ [Static]
+ [Export("withBlock:")]
+ LOTNumberBlockCallback WithBlock(LOTNumberValueCallbackBlock block);
+
+ // @property (copy, nonatomic) LOTNumberValueCallbackBlock _Nonnull callback;
+ [Export("callback", ArgumentSemantic.Copy)]
+ LOTNumberValueCallbackBlock Callback { get; set; }
+ }
+
+ // @interface LOTPointBlockCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTPointBlockCallback : LOTPointValueDelegate
+ {
+ // +(instancetype _Nonnull)withBlock:(LOTPointValueCallbackBlock _Nonnull)block;
+ [Static]
+ [Export("withBlock:")]
+ LOTPointBlockCallback WithBlock(LOTPointValueCallbackBlock block);
+
+ // @property (copy, nonatomic) LOTPointValueCallbackBlock _Nonnull callback;
+ [Export("callback", ArgumentSemantic.Copy)]
+ LOTPointValueCallbackBlock Callback { get; set; }
+ }
+
+ // @interface LOTSizeBlockCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTSizeBlockCallback : LOTSizeValueDelegate
+ {
+ // +(instancetype _Nonnull)withBlock:(LOTSizeValueCallbackBlock _Nonnull)block;
+ [Static]
+ [Export("withBlock:")]
+ LOTSizeBlockCallback WithBlock(LOTSizeValueCallbackBlock block);
+
+ // @property (copy, nonatomic) LOTSizeValueCallbackBlock _Nonnull callback;
+ [Export("callback", ArgumentSemantic.Copy)]
+ LOTSizeValueCallbackBlock Callback { get; set; }
+ }
+
+ // @interface LOTPathBlockCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTPathBlockCallback : LOTPathValueDelegate
+ {
+ // +(instancetype _Nonnull)withBlock:(LOTPathValueCallbackBlock _Nonnull)block;
+ [Static]
+ [Export("withBlock:")]
+ LOTPathBlockCallback WithBlock(LOTPathValueCallbackBlock block);
+
+ // @property (copy, nonatomic) LOTPathValueCallbackBlock _Nonnull callback;
+ [Export("callback", ArgumentSemantic.Copy)]
+ LOTPathValueCallbackBlock Callback { get; set; }
+ }
+
+ // @interface LOTPointInterpolatorCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTPointInterpolatorCallback : LOTPointValueDelegate
+ {
+ // +(instancetype _Nonnull)withFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint;
+ [Static]
+ [Export("withFromPoint:toPoint:")]
+ LOTPointInterpolatorCallback WithFromPoint(CGPoint fromPoint, CGPoint toPoint);
+
+ // @property (nonatomic) CGPoint fromPoint;
+ [Export("fromPoint", ArgumentSemantic.Assign)]
+ CGPoint FromPoint { get; set; }
+
+ // @property (nonatomic) CGPoint toPoint;
+ [Export("toPoint", ArgumentSemantic.Assign)]
+ CGPoint ToPoint { get; set; }
+
+ // @property (assign, nonatomic) CGFloat currentProgress;
+ [Export("currentProgress")]
+ nfloat CurrentProgress { get; set; }
+ }
+
+ // @interface LOTSizeInterpolatorCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTSizeInterpolatorCallback : LOTSizeValueDelegate
+ {
+ // +(instancetype _Nonnull)withFromSize:(CGSize)fromSize toSize:(CGSize)toSize;
+ [Static]
+ [Export("withFromSize:toSize:")]
+ LOTSizeInterpolatorCallback WithFromSize(CGSize fromSize, CGSize toSize);
+
+ // @property (nonatomic) CGSize fromSize;
+ [Export("fromSize", ArgumentSemantic.Assign)]
+ CGSize FromSize { get; set; }
+
+ // @property (nonatomic) CGSize toSize;
+ [Export("toSize", ArgumentSemantic.Assign)]
+ CGSize ToSize { get; set; }
+
+ // @property (assign, nonatomic) CGFloat currentProgress;
+ [Export("currentProgress")]
+ nfloat CurrentProgress { get; set; }
+ }
+
+ // @interface LOTFloatInterpolatorCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTFloatInterpolatorCallback : LOTNumberValueDelegate
+ {
+ // +(instancetype _Nonnull)withFromFloat:(CGFloat)fromFloat toFloat:(CGFloat)toFloat;
+ [Static]
+ [Export("withFromFloat:toFloat:")]
+ LOTFloatInterpolatorCallback WithFromFloat(nfloat fromFloat, nfloat toFloat);
+
+ // @property (nonatomic) CGFloat fromFloat;
+ [Export("fromFloat")]
+ nfloat FromFloat { get; set; }
+
+ // @property (nonatomic) CGFloat toFloat;
+ [Export("toFloat")]
+ nfloat ToFloat { get; set; }
+
+ // @property (assign, nonatomic) CGFloat currentProgress;
+ [Export("currentProgress")]
+ nfloat CurrentProgress { get; set; }
+ }
+
+ // @interface LOTColorValueCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTColorValueCallback : LOTColorValueDelegate
+ {
+ // +(instancetype _Nonnull)withCGColor:(CGColorRef _Nonnull)color;
+ [Static]
+ [Export("withCGColor:")]
+ unsafe LOTColorValueCallback WithCGColor(CGColor color);
+
+ // @property (nonatomic) CGColorRef _Nonnull colorValue;
+ [Export("colorValue", ArgumentSemantic.Assign)]
+ unsafe CGColor ColorValue { get; set; }
+ }
+
+ // @interface LOTNumberValueCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTNumberValueCallback : LOTNumberValueDelegate
+ {
+ // +(instancetype _Nonnull)withFloatValue:(CGFloat)numberValue;
+ [Static]
+ [Export("withFloatValue:")]
+ LOTNumberValueCallback WithFloatValue(nfloat numberValue);
+
+ // @property (assign, nonatomic) CGFloat numberValue;
+ [Export("numberValue")]
+ nfloat NumberValue { get; set; }
+ }
+
+ // @interface LOTPointValueCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTPointValueCallback : LOTPointValueDelegate
+ {
+ // +(instancetype _Nonnull)withPointValue:(CGPoint)pointValue;
+ [Static]
+ [Export("withPointValue:")]
+ LOTPointValueCallback WithPointValue(CGPoint pointValue);
+
+ // @property (assign, nonatomic) CGPoint pointValue;
+ [Export("pointValue", ArgumentSemantic.Assign)]
+ CGPoint PointValue { get; set; }
+ }
+
+ // @interface LOTSizeValueCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTSizeValueCallback : LOTSizeValueDelegate
+ {
+ // +(instancetype _Nonnull)withPointValue:(CGSize)sizeValue;
+ [Static]
+ [Export("withPointValue:")]
+ LOTSizeValueCallback WithPointValue(CGSize sizeValue);
+
+ // @property (assign, nonatomic) CGSize sizeValue;
+ [Export("sizeValue", ArgumentSemantic.Assign)]
+ CGSize SizeValue { get; set; }
+ }
+
+ // @interface LOTPathValueCallback : NSObject
+ [BaseType(typeof(NSObject))]
+ interface LOTPathValueCallback : LOTPathValueDelegate
+ {
+ // +(instancetype _Nonnull)withCGPath:(CGPathRef _Nonnull)path;
+ [Static]
+ [Export("withCGPath:")]
+ unsafe LOTPathValueCallback WithCGPath(CGPath path);
+
+ // @property (nonatomic) CGPathRef _Nonnull pathValue;
+ [Export("pathValue", ArgumentSemantic.Assign)]
+ unsafe CGPath PathValue { get; set; }
+ }
+}
diff --git a/Lottie.iOS.net6/LOTAnimationView.cs b/Lottie.iOS.net6/LOTAnimationView.cs
new file mode 100644
index 0000000..83bd7e9
--- /dev/null
+++ b/Lottie.iOS.net6/LOTAnimationView.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Threading.Tasks;
+using Foundation;
+
+namespace Airbnb.Lottie
+{
+ public partial class LOTAnimationView
+ {
+ ///
+ /// Asynchronously play the animation.
+ ///
+ public async Task PlayAsync()
+ {
+ var tcs = new TaskCompletionSource();
+
+ this.CompletionBlock = animationFinished => tcs.SetResult(true);
+
+ this.Play();
+
+ return await tcs.Task;
+ }
+
+ ///
+ /// Plays the animation from its current position to a specific progress.
+ /// The animation will start from its current position.
+ ///
+ public Task PlayToProgressAsync(nfloat toProgress)
+ {
+ var tcs = new TaskCompletionSource();
+
+ this.PlayToProgress(toProgress, (bool animationFinished) => tcs.SetResult(animationFinished));
+
+ return tcs.Task;
+ }
+
+ public Task PlayFromProgressAsync(nfloat fromStartProgress, nfloat toEndProgress)
+ {
+ var tcs = new TaskCompletionSource();
+
+ this.PlayFromProgress(fromStartProgress, toEndProgress, (bool animationFinished) => tcs.SetResult(animationFinished));
+
+ return tcs.Task;
+ }
+
+ public Task PlayToFrameAsync(NSNumber toFrame)
+ {
+ var tcs = new TaskCompletionSource();
+
+ this.PlayToFrame(toFrame, (bool animationFinished) => tcs.SetResult(animationFinished));
+
+ return tcs.Task;
+ }
+
+ public Task PlayFromFrameAsync(NSNumber fromStartFrame, NSNumber toEndFrame)
+ {
+ var tcs = new TaskCompletionSource();
+
+ this.PlayFromFrame(fromStartFrame, toEndFrame, (bool animationFinished) => tcs.SetResult(animationFinished));
+
+ return tcs.Task;
+ }
+
+ }
+}
diff --git a/Lottie.iOS.net6/Linker.cs b/Lottie.iOS.net6/Linker.cs
new file mode 100644
index 0000000..2a14bf9
--- /dev/null
+++ b/Lottie.iOS.net6/Linker.cs
@@ -0,0 +1,28 @@
+
+
+namespace DreamTeam.Xamarin.lottie_ios
+{
+ public static class ___DreamTeam_Xamarin_lottie_ios
+ {
+ static ___DreamTeam_Xamarin_lottie_ios()
+ {
+ DontLooseMeDuringBuild();
+ }
+
+ public static void DontLooseMeDuringBuild()
+ {
+
+ }
+ }
+}
+
+namespace ApiDefinitions
+{
+ partial class Messaging
+ {
+ static Messaging()
+ {
+ DreamTeam.Xamarin.lottie_ios.___DreamTeam_Xamarin_lottie_ios.DontLooseMeDuringBuild();
+ }
+ }
+}
diff --git a/Lottie.iOS.net6/Lottie.framework.linkwith.cs b/Lottie.iOS.net6/Lottie.framework.linkwith.cs
new file mode 100644
index 0000000..8ec1834
--- /dev/null
+++ b/Lottie.iOS.net6/Lottie.framework.linkwith.cs
@@ -0,0 +1,8 @@
+
+using ObjCRuntime;
+[assembly: LinkWith ("Lottie.framework", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator | LinkTarget.Arm64 | LinkTarget.Simulator64,
+Frameworks = "UIKit CoreGraphics Foundation QuartzCore",
+//IsCxx = true,
+SmartLink = true,
+LinkerFlags="-ObjC",
+ForceLoad = true)]
diff --git a/Lottie.iOS.net6/Lottie.iOS.net6.csproj b/Lottie.iOS.net6/Lottie.iOS.net6.csproj
new file mode 100644
index 0000000..1981bf7
--- /dev/null
+++ b/Lottie.iOS.net6/Lottie.iOS.net6.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ net6.0-ios
+ enable
+ true
+ Lottie.iOS
+ Lottie.iOS
+ Render After Effects animations natively on Android, iOS, MacOS, TVOs and UWP
+ Com.Airbnb.iOS.Lottie
+ false
+ true
+ 3.0.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Static
+ UIKit
+ true
+ true
+ -ObjC
+ true
+
+
+
+
diff --git a/Lottie.iOS.net6/StructsAndEnums.cs b/Lottie.iOS.net6/StructsAndEnums.cs
new file mode 100644
index 0000000..6431b26
--- /dev/null
+++ b/Lottie.iOS.net6/StructsAndEnums.cs
@@ -0,0 +1,23 @@
+using System;
+using ObjCRuntime;
+
+namespace Airbnb.Lottie
+{
+ [Native]
+ public enum LOTViewContentMode : ulong
+ {
+ ScaleToFill,
+ ScaleAspectFit,
+ ScaleAspectFill,
+ Redraw,
+ Center,
+ Top,
+ Bottom,
+ Left,
+ Right,
+ TopLeft,
+ TopRight,
+ BottomLeft,
+ BottomRight
+ }
+}
diff --git a/Lottie.iOS.net6/libLottie-ios.a b/Lottie.iOS.net6/libLottie-ios.a
new file mode 100644
index 0000000..7068d94
Binary files /dev/null and b/Lottie.iOS.net6/libLottie-ios.a differ