From 6566737e630a725de015c57ecfcaadf5942ca7a9 Mon Sep 17 00:00:00 2001 From: Daniel Luberda Date: Mon, 6 May 2019 02:27:08 +0200 Subject: [PATCH] Added IImageSourceHandler for iOS / Mac / Android --- nuget/Xamarin.FFImageLoading.Forms.nuspec | 2 +- .../Mac/AppDelegate.cs | 1 + .../iOS/AppDelegate.cs | 1 + .../Properties/AssemblyInfo.cs | 5 + .../CachedImageRenderer.cs | 8 +- .../FFImageLoading.Forms.Droid.csproj | 1 + .../FFImageLoadingImageSourceHandler.cs | 70 ++++++++++++ .../FFImageLoadingImageViewHandler.cs | 107 ++++-------------- .../FFImageLoading.Forms.Mac.csproj | 3 + .../CachedImageRenderer.cs | 15 ++- .../FFImageLoading.Forms.Touch.csproj | 1 + .../FFImageLoadingImageSourceHandler.cs | 49 ++++++++ .../FFImageLoading.Forms.csproj | 1 + .../Handlers/HandlerBase.cs | 81 +++++++++++++ .../Properties/AssemblyInfo.cs | 6 + .../FFImageLoading.Tizen.csproj | 1 + .../Properties/AssemblyInfo.cs | 8 ++ .../Properties/AssemblyInfo.cs | 5 + .../Properties/AssemblyInfo.cs | 6 + 19 files changed, 282 insertions(+), 89 deletions(-) create mode 100644 source/FFImageLoading.Forms.Droid/FFImageLoadingImageSourceHandler.cs create mode 100644 source/FFImageLoading.Forms.Touch/FFImageLoadingImageSourceHandler.cs create mode 100644 source/FFImageLoading.Forms/Handlers/HandlerBase.cs create mode 100644 source/FFImageLoading.Tizen/Properties/AssemblyInfo.cs diff --git a/nuget/Xamarin.FFImageLoading.Forms.nuspec b/nuget/Xamarin.FFImageLoading.Forms.nuspec index b05e15fc3..075ca9739 100644 --- a/nuget/Xamarin.FFImageLoading.Forms.nuspec +++ b/nuget/Xamarin.FFImageLoading.Forms.nuspec @@ -45,7 +45,7 @@ Features: - + diff --git a/samples/ImageLoading.Forms.Sample/Mac/AppDelegate.cs b/samples/ImageLoading.Forms.Sample/Mac/AppDelegate.cs index f444007c3..3c8282b82 100644 --- a/samples/ImageLoading.Forms.Sample/Mac/AppDelegate.cs +++ b/samples/ImageLoading.Forms.Sample/Mac/AppDelegate.cs @@ -29,6 +29,7 @@ public override void DidFinishLaunching(NSNotification notification) { Xamarin.Forms.Forms.Init(); CachedImageRenderer.Init(); + CachedImageRenderer.InitImageSourceHandler(); LoadApplication(new App()); base.DidFinishLaunching(notification); } diff --git a/samples/ImageLoading.Forms.Sample/iOS/AppDelegate.cs b/samples/ImageLoading.Forms.Sample/iOS/AppDelegate.cs index 1283cea05..c17aa02a9 100644 --- a/samples/ImageLoading.Forms.Sample/iOS/AppDelegate.cs +++ b/samples/ImageLoading.Forms.Sample/iOS/AppDelegate.cs @@ -14,6 +14,7 @@ public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); CachedImageRenderer.Init(); + CachedImageRenderer.InitImageSourceHandler(); var config = new FFImageLoading.Config.Configuration() { diff --git a/source/FFImageLoading.Droid/Properties/AssemblyInfo.cs b/source/FFImageLoading.Droid/Properties/AssemblyInfo.cs index 29f9ea4e8..c714b1b82 100644 --- a/source/FFImageLoading.Droid/Properties/AssemblyInfo.cs +++ b/source/FFImageLoading.Droid/Properties/AssemblyInfo.cs @@ -25,3 +25,8 @@ //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] +[assembly: InternalsVisibleTo("FFImageLoading.Tests")] +[assembly: InternalsVisibleTo("FFImageLoading.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Svg.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms.Platform")] diff --git a/source/FFImageLoading.Forms.Droid/CachedImageRenderer.cs b/source/FFImageLoading.Forms.Droid/CachedImageRenderer.cs index 4c4094596..0910bfb20 100644 --- a/source/FFImageLoading.Forms.Droid/CachedImageRenderer.cs +++ b/source/FFImageLoading.Forms.Droid/CachedImageRenderer.cs @@ -57,8 +57,8 @@ public static void Init(bool? enableFastRenderer) } /// - /// Call this after Xamarin.Forms.Init to use FFImageLoading in all Xamarin.Forms views - /// Including Xamarin.Forms.Image + /// Call this after Xamarin.Forms.Init to use FFImageLoading in all Xamarin.Forms views, + /// including Xamarin.Forms.Image /// public static void InitImageViewHandler() { @@ -68,8 +68,8 @@ public static void InitImageViewHandler() Helpers.Dependency.Register(typeof(EmbeddedResourceImageSource), typeof(FFImageLoadingImageViewHandler)); Helpers.Dependency.Register(typeof(DataUrlImageSource), typeof(FFImageLoadingImageViewHandler)); } - - private bool _isDisposed; + + private bool _isDisposed; private IScheduledWork _currentTask; private ImageSourceBinding _lastImageSource; private readonly MotionEventHelper _motionEventHelper = CachedImage.FixedAndroidMotionEventHandler ? new MotionEventHelper() : null; diff --git a/source/FFImageLoading.Forms.Droid/FFImageLoading.Forms.Droid.csproj b/source/FFImageLoading.Forms.Droid/FFImageLoading.Forms.Droid.csproj index bc9525a53..c32a72ef9 100644 --- a/source/FFImageLoading.Forms.Droid/FFImageLoading.Forms.Droid.csproj +++ b/source/FFImageLoading.Forms.Droid/FFImageLoading.Forms.Droid.csproj @@ -135,6 +135,7 @@ + diff --git a/source/FFImageLoading.Forms.Droid/FFImageLoadingImageSourceHandler.cs b/source/FFImageLoading.Forms.Droid/FFImageLoadingImageSourceHandler.cs new file mode 100644 index 000000000..a879ff7c6 --- /dev/null +++ b/source/FFImageLoading.Forms.Droid/FFImageLoadingImageSourceHandler.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Android.Content; +using Android.Graphics; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; +using FFImageLoading.Forms.Handlers; +using FFImageLoading.Work; +using FFImageLoading.Targets; + +namespace FFImageLoading.Forms.Platform +{ + public class FFImageLoadingImageSourceHandler : HandlerBase, IImageSourceHandler + { + public async Task LoadImageAsync(Xamarin.Forms.ImageSource imageSource, Context context, CancellationToken cancellationToken = default) + { + try + { + if (!IsValid(context)) + return null; + + var source = ImageSourceBinding.GetImageSourceBinding(imageSource, null); + if (source == null) + { + return null; + } + + var result = await LoadImageAsync(source, imageSource, context, cancellationToken); + var target = result.Target as BitmapTarget; + return target?.BitmapDrawable?.Bitmap; + } + catch (Exception) + { + return null; + } + } + + private static bool IsValid(Context context) + { + if (context == null || context.Handle == IntPtr.Zero) + return false; + +#pragma warning disable CS0618 // Type or member is obsolete + var activity = context as Android.App.Activity ?? (Android.App.Activity)Xamarin.Forms.Forms.Context; +#pragma warning restore CS0618 // Type or member is obsolete + if (activity != null) + { + if (activity.IsFinishing) + return false; + if (activity.IsDestroyed) + return false; + } + else + { + return false; + } + + return true; + } + + protected override IImageLoaderTask GetImageLoaderTask(TaskParameter parameters, Context imageView) + { + var target = new BitmapTarget(); + var task = ImageService.CreateTask(parameters, target); + ImageService.Instance.LoadImage(task); + return task; + } + } +} diff --git a/source/FFImageLoading.Forms.Droid/FFImageLoadingImageViewHandler.cs b/source/FFImageLoading.Forms.Droid/FFImageLoadingImageViewHandler.cs index 9c49bb8a7..03026c9e3 100644 --- a/source/FFImageLoading.Forms.Droid/FFImageLoadingImageViewHandler.cs +++ b/source/FFImageLoading.Forms.Droid/FFImageLoadingImageViewHandler.cs @@ -2,11 +2,9 @@ using System.Threading; using System.Threading.Tasks; using FFImageLoading.Work; - -#if __ANDROID__ +using Android.Widget; +using FFImageLoading.Forms.Handlers; using Xamarin.Forms.Platform.Android; -using TNativeImageView = Android.Widget.ImageView; -#endif //[assembly: Xamarin.Forms.ExportImageSourceHandler(typeof(Xamarin.Forms.FileImageSource), typeof(FFImageLoading.Forms.Platform.FFImageLoadingImageViewHandler))] //[assembly: Xamarin.Forms.ExportImageSourceHandler(typeof(Xamarin.Forms.StreamImageSource), typeof(FFImageLoading.Forms.Platform.FFImageLoadingImageViewHandler))] @@ -17,99 +15,38 @@ namespace FFImageLoading.Forms.Platform { [Preserve(AllMembers = true)] - public class FFImageLoadingImageViewHandler : IImageViewHandler + public class FFImageLoadingImageViewHandler : HandlerBase, IImageViewHandler { - public Task LoadImageAsync(Xamarin.Forms.ImageSource imagesource, TNativeImageView imageView, CancellationToken cancellationToken = default) + public Task LoadImageAsync(Xamarin.Forms.ImageSource imageSource, ImageView imageView, CancellationToken cancellationToken = default) { -#if __ANDROID__ - if (!IsValid(imageView)) - return Task.CompletedTask; -#endif - - var source = ImageSourceBinding.GetImageSourceBinding(imagesource, null); - if (source == null) + try { -#if __ANDROID__ - imageView.SetImageResource(Android.Resource.Color.Transparent); -#endif - return Task.CompletedTask; - } + if (!IsValid(imageView)) + return Task.CompletedTask; - TaskParameter imageLoader; - - if (source.ImageSource == ImageSource.Url) - { - var urlSource = (Xamarin.Forms.UriImageSource)imagesource; - imageLoader = ImageService.Instance.LoadUrl(source.Path, urlSource.CacheValidity); - - if (!urlSource.CachingEnabled) + var source = ImageSourceBinding.GetImageSourceBinding(imageSource, null); + if (source == null) { - imageLoader.WithCache(Cache.CacheType.None); + imageView.SetImageResource(Android.Resource.Color.Transparent); + return Task.CompletedTask; } + + return LoadImageAsync(source, imageSource, imageView, cancellationToken); } - else if (source.ImageSource == ImageSource.CompiledResource) - { - imageLoader = ImageService.Instance.LoadCompiledResource(source.Path); - } - else if (source.ImageSource == ImageSource.ApplicationBundle) - { - imageLoader = ImageService.Instance.LoadFileFromApplicationBundle(source.Path); - } - else if (source.ImageSource == ImageSource.Filepath) - { - imageLoader = ImageService.Instance.LoadFile(source.Path); - } - else if (source.ImageSource == ImageSource.Stream) - { - imageLoader = ImageService.Instance.LoadStream(source.Stream); - } - else if (source.ImageSource == ImageSource.EmbeddedResource) - { - imageLoader = ImageService.Instance.LoadEmbeddedResource(source.Path); - } - else + catch (Exception) { return Task.CompletedTask; } - - if (imageLoader != null) - { - var tcs = new TaskCompletionSource(); - - imageLoader - .FadeAnimation(false, false) - .Error(ex => { - tcs.TrySetException(ex); - }) - .Finish(scheduledWork => { - tcs.TrySetResult(scheduledWork); - }); - - var task = imageLoader.Into(imageView); - - if (cancellationToken != default) - cancellationToken.Register(() => - { - try - { - task?.Cancel(); - } - catch { } - }); - - return tcs.Task; - } - - return Task.CompletedTask; } -#if __ANDROID__ - private static bool IsValid(TNativeImageView imageView) + + private static bool IsValid(ImageView imageView) { if (imageView == null || imageView.Handle == IntPtr.Zero) return false; - - //NOTE: in some cases ContextThemeWrapper is Context + +#pragma warning disable CS0618 // Type or member is obsolete var activity = imageView.Context as Android.App.Activity ?? (Android.App.Activity)Xamarin.Forms.Forms.Context; +#pragma warning restore CS0618 // Type or member is obsolete if (activity != null) { if (activity.IsFinishing) @@ -124,6 +61,10 @@ private static bool IsValid(TNativeImageView imageView) return true; } -#endif + + protected override IImageLoaderTask GetImageLoaderTask(TaskParameter parameters, ImageView imageView) + { + return parameters.Into(imageView) as IImageLoaderTask; + } } } diff --git a/source/FFImageLoading.Forms.Mac/FFImageLoading.Forms.Mac.csproj b/source/FFImageLoading.Forms.Mac/FFImageLoading.Forms.Mac.csproj index c4de37e5c..280611d8c 100644 --- a/source/FFImageLoading.Forms.Mac/FFImageLoading.Forms.Mac.csproj +++ b/source/FFImageLoading.Forms.Mac/FFImageLoading.Forms.Mac.csproj @@ -78,6 +78,9 @@ ImageSourceBinding.cs + + FFImageLoadingImageSourceHandler.cs + diff --git a/source/FFImageLoading.Forms.Touch/CachedImageRenderer.cs b/source/FFImageLoading.Forms.Touch/CachedImageRenderer.cs index a9d3ac1b2..aad75e088 100644 --- a/source/FFImageLoading.Forms.Touch/CachedImageRenderer.cs +++ b/source/FFImageLoading.Forms.Touch/CachedImageRenderer.cs @@ -61,7 +61,20 @@ internal class _CachedImageRenderer #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } - protected override void Dispose(bool disposing) + /// + /// Call this after Xamarin.Forms.Init to use FFImageLoading in all Xamarin.Forms views, + /// including Xamarin.Forms.Image + /// + public static void InitImageSourceHandler() + { + Helpers.Dependency.Register(typeof(FileImageSource), typeof(FFImageLoadingImageSourceHandler)); + Helpers.Dependency.Register(typeof(StreamImageSource), typeof(FFImageLoadingImageSourceHandler)); + Helpers.Dependency.Register(typeof(UriImageSource), typeof(FFImageLoadingImageSourceHandler)); + Helpers.Dependency.Register(typeof(EmbeddedResourceImageSource), typeof(FFImageLoadingImageSourceHandler)); + Helpers.Dependency.Register(typeof(DataUrlImageSource), typeof(FFImageLoadingImageSourceHandler)); + } + + protected override void Dispose(bool disposing) { if (!_isDisposed) { diff --git a/source/FFImageLoading.Forms.Touch/FFImageLoading.Forms.Touch.csproj b/source/FFImageLoading.Forms.Touch/FFImageLoading.Forms.Touch.csproj index b131a0c08..c461d154e 100644 --- a/source/FFImageLoading.Forms.Touch/FFImageLoading.Forms.Touch.csproj +++ b/source/FFImageLoading.Forms.Touch/FFImageLoading.Forms.Touch.csproj @@ -60,6 +60,7 @@ + diff --git a/source/FFImageLoading.Forms.Touch/FFImageLoadingImageSourceHandler.cs b/source/FFImageLoading.Forms.Touch/FFImageLoadingImageSourceHandler.cs new file mode 100644 index 000000000..54599a793 --- /dev/null +++ b/source/FFImageLoading.Forms.Touch/FFImageLoadingImageSourceHandler.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FFImageLoading.Forms.Handlers; +using FFImageLoading.Work; + +#if __IOS__ +using PImage = UIKit.UIImage; +using PImageTarget = FFImageLoading.Targets.UIImageTarget; +using Xamarin.Forms.Platform.iOS; +#elif __MACOS__ +using PImage = AppKit.NSImage; +using PImageTarget = FFImageLoading.Targets.NSImageTarget; +using Xamarin.Forms.Platform.MacOS; +#endif + +namespace FFImageLoading.Forms.Platform +{ + public class FFImageLoadingImageSourceHandler : HandlerBase, IImageSourceHandler + { + public async Task LoadImageAsync(Xamarin.Forms.ImageSource imageSource, CancellationToken cancellationToken = default, float scale = 1) + { + try + { + var source = ImageSourceBinding.GetImageSourceBinding(imageSource, null); + if (source == null) + { + return null; + } + + var result = await LoadImageAsync(source, imageSource, null, cancellationToken); + var target = result?.Target as PImageTarget; + return target?.PImage; + } + catch (Exception) + { + return null; + } + } + + protected override IImageLoaderTask GetImageLoaderTask(TaskParameter parameters, object imageView) + { + var target = new PImageTarget(); + var task = ImageService.CreateTask(parameters, target); + ImageService.Instance.LoadImage(task); + return task; + } + } +} diff --git a/source/FFImageLoading.Forms/FFImageLoading.Forms.csproj b/source/FFImageLoading.Forms/FFImageLoading.Forms.csproj index 28118a653..371ccc11a 100644 --- a/source/FFImageLoading.Forms/FFImageLoading.Forms.csproj +++ b/source/FFImageLoading.Forms/FFImageLoading.Forms.csproj @@ -44,5 +44,6 @@ + diff --git a/source/FFImageLoading.Forms/Handlers/HandlerBase.cs b/source/FFImageLoading.Forms/Handlers/HandlerBase.cs new file mode 100644 index 000000000..fd6290fb7 --- /dev/null +++ b/source/FFImageLoading.Forms/Handlers/HandlerBase.cs @@ -0,0 +1,81 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FFImageLoading.Work; + +namespace FFImageLoading.Forms.Handlers +{ + public abstract class HandlerBase + { + protected virtual Task LoadImageAsync(IImageSourceBinding binding, Xamarin.Forms.ImageSource imageSource, TNativeView imageView, CancellationToken cancellationToken) + { + TaskParameter parameters = default; + + if (binding.ImageSource == ImageSource.Url) + { + var urlSource = (Xamarin.Forms.UriImageSource)imageSource; + parameters = ImageService.Instance.LoadUrl(binding.Path, urlSource.CacheValidity); + + if (!urlSource.CachingEnabled) + { + parameters.WithCache(Cache.CacheType.None); + } + } + else if (binding.ImageSource == ImageSource.CompiledResource) + { + parameters = ImageService.Instance.LoadCompiledResource(binding.Path); + } + else if (binding.ImageSource == ImageSource.ApplicationBundle) + { + parameters = ImageService.Instance.LoadFileFromApplicationBundle(binding.Path); + } + else if (binding.ImageSource == ImageSource.Filepath) + { + parameters = ImageService.Instance.LoadFile(binding.Path); + } + else if (binding.ImageSource == ImageSource.Stream) + { + parameters = ImageService.Instance.LoadStream(binding.Stream); + } + else if (binding.ImageSource == ImageSource.EmbeddedResource) + { + parameters = ImageService.Instance.LoadEmbeddedResource(binding.Path); + } + + if (parameters != default) + { + var tcs = new TaskCompletionSource(); + + parameters + .FadeAnimation(false, false) + .Error(ex => { + tcs.TrySetException(ex); + }) + .Finish(scheduledWork => { + tcs.TrySetResult(scheduledWork as IImageLoaderTask); + }); + + if (cancellationToken.IsCancellationRequested) + return Task.FromResult(null); + + var task = GetImageLoaderTask(parameters, imageView); + + if (cancellationToken != default) + cancellationToken.Register(() => + { + try + { + task?.Cancel(); + } + catch { } + }); + + return tcs.Task; + } + + return Task.FromResult(null); + } + + protected abstract IImageLoaderTask GetImageLoaderTask(TaskParameter parameters, TNativeView imageView); + } +} diff --git a/source/FFImageLoading.Mac/Properties/AssemblyInfo.cs b/source/FFImageLoading.Mac/Properties/AssemblyInfo.cs index 5f9c31a20..b851bd344 100644 --- a/source/FFImageLoading.Mac/Properties/AssemblyInfo.cs +++ b/source/FFImageLoading.Mac/Properties/AssemblyInfo.cs @@ -24,3 +24,9 @@ //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] + +[assembly: InternalsVisibleTo("FFImageLoading.Tests")] +[assembly: InternalsVisibleTo("FFImageLoading.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Svg.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms.Platform")] \ No newline at end of file diff --git a/source/FFImageLoading.Tizen/FFImageLoading.Tizen.csproj b/source/FFImageLoading.Tizen/FFImageLoading.Tizen.csproj index 469d76bd4..1b1678e47 100644 --- a/source/FFImageLoading.Tizen/FFImageLoading.Tizen.csproj +++ b/source/FFImageLoading.Tizen/FFImageLoading.Tizen.csproj @@ -35,6 +35,7 @@ + diff --git a/source/FFImageLoading.Tizen/Properties/AssemblyInfo.cs b/source/FFImageLoading.Tizen/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..83d7b10ea --- /dev/null +++ b/source/FFImageLoading.Tizen/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("FFImageLoading.Tests")] +[assembly: InternalsVisibleTo("FFImageLoading.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Svg.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms.Platform")] \ No newline at end of file diff --git a/source/FFImageLoading.Touch/Properties/AssemblyInfo.cs b/source/FFImageLoading.Touch/Properties/AssemblyInfo.cs index 17a205c89..19d0177ed 100644 --- a/source/FFImageLoading.Touch/Properties/AssemblyInfo.cs +++ b/source/FFImageLoading.Touch/Properties/AssemblyInfo.cs @@ -25,3 +25,8 @@ //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] +[assembly: InternalsVisibleTo("FFImageLoading.Tests")] +[assembly: InternalsVisibleTo("FFImageLoading.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Svg.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms.Platform")] \ No newline at end of file diff --git a/source/FFImageLoading.Windows/Properties/AssemblyInfo.cs b/source/FFImageLoading.Windows/Properties/AssemblyInfo.cs index 9d59e0e5c..23da27bd8 100644 --- a/source/FFImageLoading.Windows/Properties/AssemblyInfo.cs +++ b/source/FFImageLoading.Windows/Properties/AssemblyInfo.cs @@ -27,3 +27,9 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0")] + +[assembly: InternalsVisibleTo("FFImageLoading.Tests")] +[assembly: InternalsVisibleTo("FFImageLoading.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Svg.Platform")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms")] +[assembly: InternalsVisibleTo("FFImageLoading.Forms.Platform")] \ No newline at end of file