diff --git a/src/App/App.csproj b/src/App/App.csproj index dc29cf125..8444c1524 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; } + + var result = await _iconImage.Source.GetPlatformImageAsync(Handler.MauiContext); + if (result == null) + { + Icon_Error(sender, e); + } + else + { + Icon_Success(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 @@ namespace Bit.App.Controls // 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 @@ namespace Bit.App.Controls }); } -#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 @@ namespace Bit.App.Controls } } - 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 @@ namespace Bit.App.Controls 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 bbfdd41ee..dbff0e473 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..404e475a6 100644 --- a/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs +++ b/src/Core/Controls/CipherViewCell/CipherViewCell.xaml.cs @@ -23,7 +23,7 @@ namespace Bit.App.Controls _iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale; } - protected override CachedImage Icon => _iconImage; + protected override Image Icon => _iconImage; protected override IconLabel IconPlaceholder => _iconPlaceholderImage; @@ -40,5 +40,21 @@ namespace Bit.App.Controls ButtonCommand?.Execute(cipherItem.Cipher); } } + + private async void Image_OnLoaded(object sender, EventArgs e) + { + if (Handler?.MauiContext == null) { return; } + if (_iconImage?.Source == null) { return; } + + var result = await _iconImage.Source.GetPlatformImageAsync(Handler.MauiContext); + if (result == null) + { + Icon_Error(sender, e); + } + else + { + Icon_Success(sender, e); + } + } } } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index aa4fba0f9..a35737d6d 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -47,7 +47,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 class MauiProgram .UseMauiCompatibility() .UseMauiCameraView() .UseSkiaSharp() -#if !UT - .UseFFImageLoading() -#endif .ConfigureEffects(effects => { #if ANDROID @@ -63,13 +56,6 @@ public static class MauiProgram 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 28dee6780..41aa4ee82 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,22 @@ namespace Bit.App.Pages && 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) + }; } + return _iconImageSource; } }