mirror of
https://github.com/bitwarden/mobile
synced 2025-12-10 05:13:31 +00:00
Compare commits
5 Commits
b543459f8d
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7be665369 | ||
|
|
9de3bdf0b9 | ||
|
|
01ee126c6f | ||
|
|
6b15bfce12 | ||
|
|
583fd6ba1e |
@@ -99,7 +99,6 @@
|
|||||||
<PackageReference Include="Plugin.Fingerprint" Version="3.0.0-beta.1" />
|
<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" Version="2.88.4-preview.84" />
|
||||||
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" 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="AsyncAwaitBestPractices.MVVM" Version="6.0.6" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
||||||
<PackageReference Include="PCLCrypto" Version="2.1.40-alpha" />
|
<PackageReference Include="PCLCrypto" Version="2.1.40-alpha" />
|
||||||
|
|||||||
@@ -18,17 +18,16 @@
|
|||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
</controls:BaseCipherViewCell.Resources>
|
</controls:BaseCipherViewCell.Resources>
|
||||||
|
|
||||||
<controls:CachedImage
|
<Image
|
||||||
x:Name="_iconImage"
|
x:Name="_iconImage"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
BitmapOptimizations="True"
|
IsOpaque="True"
|
||||||
HorizontalOptions="Center"
|
HorizontalOptions="Center"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
Success="Icon_Success"
|
Loaded="Image_OnLoaded"
|
||||||
Error="Icon_Error"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
<controls:IconLabel
|
<controls:IconLabel
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Bit.App.Controls
|
using Bit.Core.Services;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class AuthenticatorViewCell : BaseCipherViewCell
|
public partial class AuthenticatorViewCell : BaseCipherViewCell
|
||||||
{
|
{
|
||||||
@@ -7,8 +9,36 @@
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override CachedImage Icon => _iconImage;
|
protected override Image Icon => _iconImage;
|
||||||
|
|
||||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
public abstract class BaseCipherViewCell : ExtendedGrid
|
public abstract class BaseCipherViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
protected virtual CachedImage Icon { get; }
|
protected virtual Image Icon { get; }
|
||||||
|
|
||||||
protected virtual IconLabel IconPlaceholder { 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.
|
// 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
|
// 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
|
// 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()
|
protected override void OnBindingContextChanged()
|
||||||
{
|
{
|
||||||
@@ -47,8 +47,7 @@ namespace Bit.App.Controls
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !UT
|
public void Icon_Success(object sender, EventArgs e)
|
||||||
public void Icon_Success(object sender, FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
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)
|
if (BindingContext is CipherItemViewModel cipherItemVM)
|
||||||
{
|
{
|
||||||
@@ -74,38 +73,5 @@ namespace Bit.App.Controls
|
|||||||
IconPlaceholder.IsVisible = true;
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,18 +29,17 @@
|
|||||||
<ColumnDefinition Width="60" />
|
<ColumnDefinition Width="60" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:CachedImage
|
<Image
|
||||||
x:Name="_iconImage"
|
x:Name="_iconImage"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
BitmapOptimizations="True"
|
IsOpaque="True"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
Margin="9"
|
Margin="9"
|
||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
Aspect="AspectFit"
|
Aspect="AspectFit"
|
||||||
Success="Icon_Success"
|
Loaded="Image_OnLoaded"
|
||||||
Error="Icon_Error"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False"
|
AutomationProperties.IsInAccessibleTree="False"
|
||||||
AutomationId="CipherWebsiteIcon" />
|
AutomationId="CipherWebsiteIcon" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Pages;
|
using Bit.App.Pages;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
@@ -23,7 +24,7 @@ namespace Bit.App.Controls
|
|||||||
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override CachedImage Icon => _iconImage;
|
protected override Image Icon => _iconImage;
|
||||||
|
|
||||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
||||||
|
|
||||||
@@ -40,5 +41,33 @@ namespace Bit.App.Controls
|
|||||||
ButtonCommand?.Execute(cipherItem.Cipher);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
<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 -->
|
<!-- 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>
|
||||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
|
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
|
||||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
using Bit.App.Controls;
|
|
||||||
using Camera.MAUI;
|
using Camera.MAUI;
|
||||||
using CommunityToolkit.Maui;
|
using CommunityToolkit.Maui;
|
||||||
#if !UT
|
|
||||||
using FFImageLoading.Maui;
|
|
||||||
#endif
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Maui.Controls.Compatibility.Hosting;
|
using Microsoft.Maui.Controls.Compatibility.Hosting;
|
||||||
using Microsoft.Maui.Handlers;
|
|
||||||
using SkiaSharp.Views.Maui.Controls.Hosting;
|
using SkiaSharp.Views.Maui.Controls.Hosting;
|
||||||
using AppEffects = Bit.App.Effects;
|
using AppEffects = Bit.App.Effects;
|
||||||
|
|
||||||
@@ -26,9 +22,6 @@ public static class MauiProgram
|
|||||||
.UseMauiCompatibility()
|
.UseMauiCompatibility()
|
||||||
.UseMauiCameraView()
|
.UseMauiCameraView()
|
||||||
.UseSkiaSharp()
|
.UseSkiaSharp()
|
||||||
#if !UT
|
|
||||||
.UseFFImageLoading()
|
|
||||||
#endif
|
|
||||||
.ConfigureEffects(effects =>
|
.ConfigureEffects(effects =>
|
||||||
{
|
{
|
||||||
#if ANDROID
|
#if ANDROID
|
||||||
@@ -63,13 +56,6 @@ public static class MauiProgram
|
|||||||
builder.Logging.AddDebug();
|
builder.Logging.AddDebug();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ExplicitlyPreventThingsGetRemovedBecauseOfLinker();
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExplicitlyPreventThingsGetRemovedBecauseOfLinker()
|
|
||||||
{
|
|
||||||
StubBaseCipherViewCellSoLinkerDoesntRemoveMethods.CallThisSoLinkerDoesntRemoveMethods();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Bit.App.Pages
|
|||||||
public class CipherItemViewModel : ExtendedViewModel, IGroupingsPageListItem
|
public class CipherItemViewModel : ExtendedViewModel, IGroupingsPageListItem
|
||||||
{
|
{
|
||||||
private readonly bool _websiteIconsEnabled;
|
private readonly bool _websiteIconsEnabled;
|
||||||
private string _iconImageSource = string.Empty;
|
private UriImageSource _iconImageSource = null;
|
||||||
|
|
||||||
public CipherItemViewModel(CipherView cipherView, bool websiteIconsEnabled, bool fuzzyAutofill = false)
|
public CipherItemViewModel(CipherView cipherView, bool websiteIconsEnabled, bool fuzzyAutofill = false)
|
||||||
{
|
{
|
||||||
@@ -27,14 +27,23 @@ namespace Bit.App.Pages
|
|||||||
&& IconImageSource != null;
|
&& IconImageSource != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string IconImageSource
|
public UriImageSource IconImageSource
|
||||||
{
|
{
|
||||||
get
|
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;
|
return _iconImageSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user