File: Android\FastRenderers\ImageElementManager.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Android.Widget;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using ARect = Android.Graphics.Rect;
using AScaleType = Android.Widget.ImageView.ScaleType;
using AViewCompat = AndroidX.Core.View.ViewCompat;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers
{
	public static class ImageElementManager
	{
		public static void Init(IVisualElementRenderer renderer)
		{
			renderer.ElementPropertyChanged += OnElementPropertyChanged;
			renderer.ElementChanged += OnElementChanged;
 
			if (renderer is ILayoutChanges layoutChanges)
				layoutChanges.LayoutChange += OnLayoutChange;
		}
 
		static void OnLayoutChange(object sender, global::Android.Views.View.LayoutChangeEventArgs e)
		{
			if (sender is IVisualElementRenderer renderer && renderer.View is ImageView imageView)
#pragma warning disable CS0618 // Obsolete
				AViewCompat.SetClipBounds(imageView, imageView.GetScaleType() == AScaleType.CenterCrop ? new ARect(0, 0, e.Right - e.Left, e.Bottom - e.Top) : null);
#pragma warning restore CS0618 // Obsolete
 
		}
 
		public static void Dispose(IVisualElementRenderer renderer)
		{
			renderer.ElementPropertyChanged -= OnElementPropertyChanged;
			renderer.ElementChanged -= OnElementChanged;
			if (renderer is ILayoutChanges layoutChanges)
				layoutChanges.LayoutChange -= OnLayoutChange;
 
			if (renderer is IImageRendererController imageRenderer)
				imageRenderer.SetFormsAnimationDrawable(null);
 
			if (renderer.View is ImageView imageView)
			{
				imageView.SetImageDrawable(null);
				imageView.Reset();
			}
		}
 
		async static void OnElementChanged(object sender, VisualElementChangedEventArgs e)
		{
			var renderer = (sender as IVisualElementRenderer);
			var view = renderer.View as ImageView;
			var newImageElementManager = e.NewElement as IImageElement;
			var oldImageElementManager = e.OldElement as IImageElement;
			var rendererController = renderer as IImageRendererController;
 
			if (rendererController.IsDisposed)
				return;
 
			await TryUpdateBitmap(rendererController, view, newImageElementManager, oldImageElementManager);
 
			if (rendererController.IsDisposed)
				return;
 
			UpdateAspect(rendererController, view, newImageElementManager, oldImageElementManager);
 
			if (rendererController.IsDisposed)
				return;
 
			ElevationHelper.SetElevation(view, renderer.Element);
		}
 
		async static void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			var renderer = (sender as IVisualElementRenderer);
			var ImageElementManager = (IImageElement)renderer.Element;
			var imageController = (IImageController)renderer.Element;
 
 
			if (renderer?.View?.LayoutParameters == null && (renderer is ILayoutChanges lc && lc.HasLayoutOccurred))
			{
				return;
			}
 
			if (e.IsOneOf(Image.SourceProperty, Button.ImageSourceProperty))
			{
				await TryUpdateBitmap(renderer as IImageRendererController, (ImageView)renderer.View, (IImageElement)renderer.Element).ConfigureAwait(false);
 
			}
			else if (e.Is(Image.AspectProperty))
			{
				UpdateAspect(renderer as IImageRendererController, (ImageView)renderer.View, (IImageElement)renderer.Element);
			}
			else if (e.Is(Image.IsAnimationPlayingProperty))
				await StartStopAnimation(renderer, imageController, ImageElementManager).ConfigureAwait(false);
		}
 
		async static Task StartStopAnimation(
			IVisualElementRenderer renderer,
			IImageController imageController,
			IImageElement imageElement)
		{
			IImageRendererController imageRendererController = renderer as IImageRendererController;
			var view = renderer.View as ImageView;
			if (imageRendererController.IsDisposed || imageElement == null || view == null || view.IsDisposed())
				return;
 
			if (imageElement.IsLoading)
				return;
 
			if (!(view.Drawable is FormsAnimationDrawable) && imageElement.IsAnimationPlaying)
				await TryUpdateBitmap(imageRendererController, view, imageElement);
 
			if (view.Drawable is FormsAnimationDrawable animation)
			{
				if (imageElement.IsAnimationPlaying && !animation.IsRunning)
					animation.Start();
				else if (!imageElement.IsAnimationPlaying && animation.IsRunning)
					animation.Stop();
			}
		}
 
 
		async static Task TryUpdateBitmap(IImageRendererController rendererController, ImageView Control, IImageElement newImage, IImageElement previous = null)
		{
			if (newImage == null || rendererController.IsDisposed)
			{
				return;
			}
 
			if (Control.Drawable is FormsAnimationDrawable currentAnimation)
			{
				rendererController.SetFormsAnimationDrawable(currentAnimation);
				currentAnimation.Stop();
			}
			else
			{
				rendererController.SetFormsAnimationDrawable(null);
			}
 
			try
			{
				await Control.UpdateBitmap(newImage, previous).ConfigureAwait(false);
			}
			catch (Exception ex)
			{
				Application.Current?.FindMauiContext()?.CreateLogger<IImageRendererController>()?.LogWarning(ex, "Error loading image");
			}
			finally
			{
				if (newImage is IImageController imageController)
					imageController.SetIsLoading(false);
			}
 
			if (rendererController.IsDisposed)
				return;
 
			if (Control.Drawable is FormsAnimationDrawable updatedAnimation)
			{
				rendererController.SetFormsAnimationDrawable(updatedAnimation);
 
				if (newImage.IsAnimationPlaying)
					updatedAnimation.Start();
			}
		}
 
		internal static void OnAnimationStopped(IElementController image, FormsAnimationDrawableStateEventArgs e)
		{
			if (image != null && e.Finished)
				image.SetValueFromRenderer(Image.IsAnimationPlayingProperty, false);
		}
 
		static void UpdateAspect(IImageRendererController rendererController, ImageView Control, IImageElement newImage, IImageElement previous = null)
		{
			if (newImage == null || rendererController.IsDisposed)
			{
				return;
			}
 
			ImageView.ScaleType type = newImage.Aspect.ToScaleType();
			Control.SetScaleType(type);
		}
	}
}