This commit is contained in:
Dinis Vieira 2024-04-24 08:51:06 -07:00 committed by GitHub
commit 63f5518f3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 86 additions and 112 deletions

View File

@ -99,7 +99,6 @@
<PackageReference Include="Plugin.Fingerprint" Version="3.0.0-beta.1" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.4-preview.84" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" Version="2.88.4-preview.84" />
<PackageReference Include="FFImageLoadingCompat.Maui" Version="0.1.1" />
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="6.0.6" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="PCLCrypto" Version="2.1.40-alpha" />

View File

@ -18,17 +18,16 @@
<u:InverseBoolConverter x:Key="inverseBool" />
</controls:BaseCipherViewCell.Resources>
<controls:CachedImage
<Image
x:Name="_iconImage"
Grid.Column="0"
Grid.RowSpan="2"
BitmapOptimizations="True"
IsOpaque="True"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
Success="Icon_Success"
Error="Icon_Error"
Loaded="Image_OnLoaded"
AutomationProperties.IsInAccessibleTree="False" />
<controls:IconLabel

View File

@ -1,4 +1,6 @@
namespace Bit.App.Controls
using Bit.Core.Services;
namespace Bit.App.Controls
{
public partial class AuthenticatorViewCell : BaseCipherViewCell
{
@ -7,8 +9,36 @@
InitializeComponent();
}
protected override CachedImage Icon => _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);
}
}
}
}

View File

@ -1,42 +0,0 @@
namespace Bit.App.Controls
{
#if !UT
public class CachedImage : FFImageLoading.Maui.CachedImage
{
}
#else
/// <summary>
/// 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.
/// </summary>
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
}

View File

@ -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
}
}
}

View File

@ -29,18 +29,17 @@
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:CachedImage
<Image
x:Name="_iconImage"
Grid.Column="0"
BitmapOptimizations="True"
IsOpaque="True"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Margin="9"
WidthRequest="22"
HeightRequest="22"
Aspect="AspectFit"
Success="Icon_Success"
Error="Icon_Error"
Loaded="Image_OnLoaded"
AutomationProperties.IsInAccessibleTree="False"
AutomationId="CipherWebsiteIcon" />

View File

@ -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 @@ 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 +41,33 @@ 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; }
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);
}
}
}
}

View File

@ -47,7 +47,6 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<!-- HACK: When running Unit Tests we cannot load FFImageLoading because it doesn't support "raw" net8.0 -->
<PackageReference Condition="!$(CustomConstants.Contains(UT))" Include="FFImageLoadingCompat.Maui" Version="0.1.1" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />

View File

@ -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();
}
}

View File

@ -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 @@ 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),
CachingEnabled = true
};
}
return _iconImageSource;
}
}