diff --git a/src/App/App.csproj b/src/App/App.csproj index 4de5cea2a..63f1a9b76 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -99,7 +99,6 @@ - diff --git a/src/Core/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml b/src/Core/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml index 99c56791c..b93603bea 100644 --- a/src/Core/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml +++ b/src/Core/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml @@ -18,17 +18,16 @@ - _iconImage; + protected override Image Icon => _iconImage; protected override IconLabel IconPlaceholder => _iconPlaceholderImage; + + private async void Image_OnLoaded(object sender, EventArgs e) + { + if (Handler?.MauiContext == null) { return; } + if (_iconImage?.Source == null) { return; } + + try + { + var result = await _iconImage.Source.GetPlatformImageAsync(Handler.MauiContext); + if (result == null) + { + Icon_Error(sender, e); + } + else + { + Icon_Success(sender, e); + } + } + catch (InvalidOperationException) //Can occur with incorrect/malformed uris + { + Icon_Error(sender, e); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + Icon_Error(sender, e); + } + } } } diff --git a/src/Core/Controls/CachedImage.cs b/src/Core/Controls/CachedImage.cs deleted file mode 100644 index 3f95dc1bf..000000000 --- a/src/Core/Controls/CachedImage.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Bit.App.Controls -{ -#if !UT - public class CachedImage : FFImageLoading.Maui.CachedImage - { - } -#else - /// - /// Given that FFImageLoading package doesn't support net8.0 then for Unit tests projects to build and run correctly - /// we need to not include the reference to FFImageLoading and therefore wrap this class - /// to provide a stub one that does nothing so this project doesn't break and we can run the tests. - /// - public class CachedImage : View - { - public static readonly BindableProperty SourceProperty = BindableProperty.Create( - nameof(Source), typeof(ImageSource), typeof(CachedImage)); - - public static readonly BindableProperty AspectProperty = BindableProperty.Create( - nameof(Aspect), typeof(Aspect), typeof(CachedImage)); - - public bool BitmapOptimizations { get; set; } - public string ErrorPlaceholder { get; set; } - public string LoadingPlaceholder { get; set; } - - public ImageSource Source - { - get { return (ImageSource)GetValue(SourceProperty); } - set { SetValue(SourceProperty, value); } - } - public Aspect Aspect - { - get { return (Aspect)GetValue(AspectProperty); } - set { SetValue(AspectProperty, value); } - } - - public bool IsLoading { get; set; } - - public event EventHandler Success; - public event EventHandler Error; - } -#endif -} diff --git a/src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs b/src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs index 78dbf82ad..baa952127 100644 --- a/src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs +++ b/src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs @@ -4,7 +4,7 @@ namespace Bit.App.Controls { public abstract class BaseCipherViewCell : ExtendedGrid { - protected virtual CachedImage Icon { get; } + protected virtual Image Icon { get; } protected virtual IconLabel IconPlaceholder { get; } @@ -15,7 +15,7 @@ public abstract class BaseCipherViewCell : ExtendedGrid // the app would crash because there can't be any lock files by the app when it gets suspended. // So, the approach has changed to reuse the IconLabel default icon to use it for these placeholders // as well. In order to do that both icon controls change their visibility dynamically here reacting to - // CachedImage events and binding context changes. + // Image OnLoaded event and binding context changes. protected override void OnBindingContextChanged() { @@ -47,8 +47,7 @@ private void UpdateIconImages(bool showIcon) }); } -#if !UT - public void Icon_Success(object sender, FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs e) + public void Icon_Success(object sender, EventArgs e) { if (BindingContext is CipherItemViewModel cipherItemVM) { @@ -62,7 +61,7 @@ public void Icon_Success(object sender, FFImageLoading.Maui.CachedImageEvents.Su } } - public void Icon_Error(object sender, FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs e) + public void Icon_Error(object sender, EventArgs e) { if (BindingContext is CipherItemViewModel cipherItemVM) { @@ -74,38 +73,5 @@ public void Icon_Error(object sender, FFImageLoading.Maui.CachedImageEvents.Erro IconPlaceholder.IsVisible = true; }); } -#else - private void Icon_Success(object sender, EventArgs e) {} - private void Icon_Error(object sender, EventArgs e) {} -#endif - } - - public class StubBaseCipherViewCellSoLinkerDoesntRemoveMethods : BaseCipherViewCell - { - protected override CachedImage Icon => new CachedImage(); - protected override IconLabel IconPlaceholder => new IconLabel(); - - public static void CallThisSoLinkerDoesntRemoveMethods() - { -#if !UT - var stub = new StubBaseCipherViewCellSoLinkerDoesntRemoveMethods(); - - try - { - stub.Icon_Success(stub, new FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs(new FFImageLoading.Work.ImageInformation(), FFImageLoading.Work.LoadingResult.Disk)); - } - catch (Exception) - { - } - - try - { - stub.Icon_Error(stub, new FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs(new InvalidOperationException("stub"))); - } - catch (Exception) - { - } -#endif - } } } diff --git a/src/Core/Controls/CipherViewCell/CipherViewCell.xaml b/src/Core/Controls/CipherViewCell/CipherViewCell.xaml index 4a8ebaeba..99da933aa 100644 --- a/src/Core/Controls/CipherViewCell/CipherViewCell.xaml +++ b/src/Core/Controls/CipherViewCell/CipherViewCell.xaml @@ -29,18 +29,17 @@ - diff --git a/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs b/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs index fd163bce3..ed6b78339 100644 --- a/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs +++ b/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs @@ -1,6 +1,7 @@ using System.Windows.Input; using Bit.App.Abstractions; using Bit.App.Pages; +using Bit.Core.Services; using Bit.Core.Utilities; namespace Bit.App.Controls @@ -23,7 +24,7 @@ public CipherViewCell() _iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale; } - protected override CachedImage Icon => _iconImage; + protected override Image Icon => _iconImage; protected override IconLabel IconPlaceholder => _iconPlaceholderImage; @@ -40,5 +41,33 @@ private void MoreButton_Clicked(object sender, EventArgs e) ButtonCommand?.Execute(cipherItem.Cipher); } } + + private async void Image_OnLoaded(object sender, EventArgs e) + { + if (Handler?.MauiContext == null) { return; } + if (_iconImage?.Source == null) { return; } + + try + { + var result = await _iconImage.Source.GetPlatformImageAsync(Handler.MauiContext); + if (result == null) + { + Icon_Error(sender, e); + } + else + { + Icon_Success(sender, e); + } + } + catch (InvalidOperationException) //Can occur with incorrect/malformed uris + { + Icon_Error(sender, e); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + Icon_Error(sender, e); + } + } } } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 3f880f911..09db4bf31 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -48,7 +48,6 @@ - diff --git a/src/Core/MauiProgram.cs b/src/Core/MauiProgram.cs index 43fdfd843..5020dd82a 100644 --- a/src/Core/MauiProgram.cs +++ b/src/Core/MauiProgram.cs @@ -1,12 +1,8 @@ -using Bit.App.Controls; + using Camera.MAUI; using CommunityToolkit.Maui; -#if !UT -using FFImageLoading.Maui; -#endif using Microsoft.Extensions.Logging; using Microsoft.Maui.Controls.Compatibility.Hosting; -using Microsoft.Maui.Handlers; using SkiaSharp.Views.Maui.Controls.Hosting; using AppEffects = Bit.App.Effects; @@ -26,9 +22,6 @@ public static MauiAppBuilder ConfigureMauiAppBuilder(Action cus .UseMauiCompatibility() .UseMauiCameraView() .UseSkiaSharp() -#if !UT - .UseFFImageLoading() -#endif .ConfigureEffects(effects => { #if ANDROID @@ -63,13 +56,6 @@ public static MauiAppBuilder ConfigureMauiAppBuilder(Action cus builder.Logging.AddDebug(); #endif - ExplicitlyPreventThingsGetRemovedBecauseOfLinker(); - return builder; } - - private static void ExplicitlyPreventThingsGetRemovedBecauseOfLinker() - { - StubBaseCipherViewCellSoLinkerDoesntRemoveMethods.CallThisSoLinkerDoesntRemoveMethods(); - } } diff --git a/src/Core/Pages/Vault/CipherItemViewModel.cs b/src/Core/Pages/Vault/CipherItemViewModel.cs index a5f094d9d..5136e81d2 100644 --- a/src/Core/Pages/Vault/CipherItemViewModel.cs +++ b/src/Core/Pages/Vault/CipherItemViewModel.cs @@ -7,7 +7,7 @@ namespace Bit.App.Pages public class CipherItemViewModel : ExtendedViewModel, IGroupingsPageListItem { private readonly bool _websiteIconsEnabled; - private string _iconImageSource = string.Empty; + private UriImageSource _iconImageSource = null; public CipherItemViewModel(CipherView cipherView, bool websiteIconsEnabled, bool fuzzyAutofill = false) { @@ -27,14 +27,23 @@ public bool ShowIconImage && IconImageSource != null; } - public string IconImageSource + public UriImageSource IconImageSource { get { - if (_iconImageSource == string.Empty) // default value since icon source can return null + if (_iconImageSource == null) // default value since icon source can return null { - _iconImageSource = IconImageHelper.GetIconImage(Cipher); + var iconImageStr = IconImageHelper.GetIconImage(Cipher); + if (string.IsNullOrWhiteSpace(iconImageStr)) { return null; } + + _iconImageSource = new UriImageSource + { + Uri = new Uri(iconImageStr), + CacheValidity = TimeSpan.FromDays(90), + CachingEnabled = true + }; } + return _iconImageSource; } }