mirror of
https://github.com/bitwarden/mobile
synced 2026-01-08 03:23:23 +00:00
Merge branch 'feature/maui-migration' into feature/maui-migration-passkeys
This commit is contained in:
@@ -76,7 +76,8 @@ namespace Bit.Droid
|
||||
|
||||
//We need to get and set the Options before calling OnCreate as that will "trigger" CreateWindow on App.xaml.cs
|
||||
_appOptions = GetOptions();
|
||||
((Bit.App.App)Microsoft.Maui.Controls.Application.Current).SetOptions(_appOptions);
|
||||
//This does not replace existing Options in App.xaml.cs if it exists already. It only updates properties in Options related with Autofill/CreateSend/etc..
|
||||
((Bit.App.App)Microsoft.Maui.Controls.Application.Current).SetAndroidOptions(_appOptions);
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
|
||||
@@ -460,21 +460,20 @@ namespace Bit.iOS
|
||||
_eventTimer?.Invalidate();
|
||||
_eventTimer?.Dispose();
|
||||
_eventTimer = null;
|
||||
// TODO: Uncomment, this is just a test to see if this is causing the background crash on release when sending the app to background
|
||||
//MainThread.BeginInvokeOnMainThread(() =>
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// _eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
||||
// {
|
||||
// _eventService?.UploadEventsAsync().FireAndForget();
|
||||
// });
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
// }
|
||||
//});
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
||||
{
|
||||
_eventService?.UploadEventsAsync().FireAndForget();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task StopEventTimerAsync()
|
||||
@@ -484,20 +483,19 @@ namespace Bit.iOS
|
||||
_eventTimer?.Invalidate();
|
||||
_eventTimer?.Dispose();
|
||||
_eventTimer = null;
|
||||
// TODO: Uncomment, this is just a test to see if this is causing the background crash on release when sending the app to background
|
||||
//if (_eventBackgroundTaskId > 0)
|
||||
//{
|
||||
// UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
// _eventBackgroundTaskId = 0;
|
||||
//}
|
||||
//_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
//{
|
||||
// UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
// _eventBackgroundTaskId = 0;
|
||||
//});
|
||||
//await _eventService.UploadEventsAsync();
|
||||
//UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
//_eventBackgroundTaskId = 0;
|
||||
if (_eventBackgroundTaskId > 0)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
}
|
||||
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
});
|
||||
await _eventService.UploadEventsAsync();
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -85,10 +85,27 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
|
||||
//Allows setting Options from MainActivity before base.OnCreate
|
||||
public void SetOptions(AppOptions appOptions)
|
||||
/// <summary>
|
||||
/// Allows setting Options from MainActivity before base.OnCreate
|
||||
/// Note 1: This is only be used by Android due to way it's Lifecycle works
|
||||
/// Note 2: This method does not replace existing Options in App.xaml.cs if it exists already.
|
||||
/// It only updates properties in Options related with Autofill/CreateSend/etc..
|
||||
/// </summary>
|
||||
/// <param name="appOptions">Options created in Android MainActivity.cs</param>
|
||||
public void SetAndroidOptions(AppOptions appOptions)
|
||||
{
|
||||
Options = appOptions ?? new AppOptions();
|
||||
if (Options == null)
|
||||
{
|
||||
Options = appOptions ?? new AppOptions();
|
||||
}
|
||||
else if(appOptions != null)
|
||||
{
|
||||
Options.Uri = appOptions.Uri;
|
||||
Options.MyVaultTile = appOptions.MyVaultTile;
|
||||
Options.GeneratorTile = appOptions.GeneratorTile;
|
||||
Options.FromAutofillFramework = appOptions.FromAutofillFramework;
|
||||
Options.CreateSend = appOptions.CreateSend;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState activationState)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
<controls:BaseCipherViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Controls.AuthenticatorViewCell"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
@@ -13,34 +13,33 @@
|
||||
RowSpacing="0"
|
||||
Padding="0,10,0,0"
|
||||
RowDefinitions="*,*">
|
||||
<Grid.Resources>
|
||||
<controls:BaseCipherViewCell.Resources>
|
||||
<u:IconGlyphConverter x:Key="iconGlyphConverter" />
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
</Grid.Resources>
|
||||
|
||||
<controls:IconLabel
|
||||
Grid.Column="0"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
StyleClass="list-icon, list-icon-platform"
|
||||
Grid.RowSpan="2"
|
||||
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||
AutomationProperties.IsInAccessibleTree="False" />
|
||||
</controls:BaseCipherViewCell.Resources>
|
||||
|
||||
<controls:CachedImage
|
||||
x:Name="_iconImage"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="2"
|
||||
BitmapOptimizations="True"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
WidthRequest="22"
|
||||
HeightRequest="22"
|
||||
Grid.RowSpan="2"
|
||||
IsVisible="{Binding ShowIconImage}"
|
||||
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||
Success="Icon_Success"
|
||||
Error="Icon_Error"
|
||||
AutomationProperties.IsInAccessibleTree="False" />
|
||||
|
||||
<controls:IconLabel
|
||||
x:Name="_iconPlaceholderImage"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
StyleClass="list-icon, list-icon-platform"
|
||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||
AutomationProperties.IsInAccessibleTree="False" />
|
||||
<!-- ErrorPlaceholder="login.png"
|
||||
LoadingPlaceholder="login.png" -->
|
||||
|
||||
<Label
|
||||
LineBreakMode="TailTruncation"
|
||||
@@ -124,4 +123,4 @@
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
SemanticProperties.Description="{u:I18n CopyTotp}" />
|
||||
</controls:ExtendedGrid>
|
||||
</controls:BaseCipherViewCell>
|
||||
@@ -1,10 +1,14 @@
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public partial class AuthenticatorViewCell : ExtendedGrid
|
||||
public partial class AuthenticatorViewCell : BaseCipherViewCell
|
||||
{
|
||||
public AuthenticatorViewCell()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override CachedImage Icon => _iconImage;
|
||||
|
||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
||||
}
|
||||
}
|
||||
|
||||
103
src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs
Normal file
103
src/Core/Controls/CipherViewCell/BaseCipherViewCell.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Bit.App.Pages;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public abstract class BaseCipherViewCell : ExtendedGrid
|
||||
{
|
||||
protected virtual CachedImage Icon { get; }
|
||||
|
||||
protected virtual IconLabel IconPlaceholder { get; }
|
||||
|
||||
// HACK: PM-5896 Fix for Background Crash on iOS
|
||||
// While loading the cipher icon and the user sent the app to background
|
||||
// the app was crashing sometimes when the "LoadingPlaceholder" or "ErrorPlaceholder"
|
||||
// were being accessed, thus locked, and as soon the app got suspended by the OS
|
||||
// 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.
|
||||
|
||||
protected override void OnBindingContextChanged()
|
||||
{
|
||||
Icon.Source = null;
|
||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
||||
{
|
||||
Icon.Source = cipherItemVM.IconImageSource;
|
||||
if (!cipherItemVM.IconImageSuccesfullyLoaded)
|
||||
{
|
||||
UpdateIconImages(cipherItemVM.ShowIconImage);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnBindingContextChanged();
|
||||
}
|
||||
|
||||
private void UpdateIconImages(bool showIcon)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (!showIcon)
|
||||
{
|
||||
Icon.IsVisible = false;
|
||||
IconPlaceholder.IsVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
IconPlaceholder.IsVisible = Icon.IsLoading;
|
||||
});
|
||||
}
|
||||
|
||||
public void Icon_Success(object sender, FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs e)
|
||||
{
|
||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
||||
{
|
||||
cipherItemVM.IconImageSuccesfullyLoaded = true;
|
||||
}
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Icon.IsVisible = true;
|
||||
IconPlaceholder.IsVisible = false;
|
||||
});
|
||||
}
|
||||
|
||||
public void Icon_Error(object sender, FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs e)
|
||||
{
|
||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
||||
{
|
||||
cipherItemVM.IconImageSuccesfullyLoaded = false;
|
||||
}
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Icon.IsVisible = false;
|
||||
IconPlaceholder.IsVisible = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class StubBaseCipherViewCellSoLinkerDoesntRemoveMethods : BaseCipherViewCell
|
||||
{
|
||||
protected override CachedImage Icon => new CachedImage();
|
||||
protected override IconLabel IconPlaceholder => new IconLabel();
|
||||
|
||||
public static void CallThisSoLinkerDoesntRemoveMethods()
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
<controls:BaseCipherViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Controls.CipherViewCell"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
@@ -29,17 +29,6 @@
|
||||
<ColumnDefinition Width="60" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:IconLabel
|
||||
Grid.Column="0"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
StyleClass="list-icon, list-icon-platform"
|
||||
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
||||
AutomationProperties.IsInAccessibleTree="False"
|
||||
AutomationId="CipherTypeIcon" />
|
||||
|
||||
<controls:CachedImage
|
||||
x:Name="_iconImage"
|
||||
Grid.Column="0"
|
||||
@@ -50,12 +39,21 @@
|
||||
WidthRequest="22"
|
||||
HeightRequest="22"
|
||||
Aspect="AspectFit"
|
||||
IsVisible="{Binding ShowIconImage}"
|
||||
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||
Success="Icon_Success"
|
||||
Error="Icon_Error"
|
||||
AutomationProperties.IsInAccessibleTree="False"
|
||||
AutomationId="CipherWebsiteIcon" />
|
||||
<!-- ErrorPlaceholder="login.png"
|
||||
LoadingPlaceholder="login.png" -->
|
||||
|
||||
<controls:IconLabel
|
||||
x:Name="_iconPlaceholderImage"
|
||||
Grid.Column="0"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
StyleClass="list-icon, list-icon-platform"
|
||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
||||
AutomationProperties.IsInAccessibleTree="False"
|
||||
AutomationId="CipherTypeIcon" />
|
||||
|
||||
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -121,4 +119,4 @@
|
||||
SemanticProperties.Description="{u:I18n Options}"
|
||||
AutomationId="CipherOptionsButton" />
|
||||
|
||||
</controls:ExtendedGrid>
|
||||
</controls:BaseCipherViewCell>
|
||||
@@ -5,7 +5,7 @@ using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public partial class CipherViewCell : ExtendedGrid
|
||||
public partial class CipherViewCell : BaseCipherViewCell
|
||||
{
|
||||
private const int ICON_COLUMN_DEFAULT_WIDTH = 40;
|
||||
private const int ICON_IMAGE_DEFAULT_WIDTH = 22;
|
||||
@@ -23,6 +23,10 @@ namespace Bit.App.Controls
|
||||
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
||||
}
|
||||
|
||||
protected override CachedImage Icon => _iconImage;
|
||||
|
||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
||||
|
||||
public ICommand ButtonCommand
|
||||
{
|
||||
get => GetValue(ButtonCommandProperty) as ICommand;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Camera.MAUI;
|
||||
using Bit.App.Controls;
|
||||
using Camera.MAUI;
|
||||
using CommunityToolkit.Maui;
|
||||
#if !UT
|
||||
using FFImageLoading.Maui;
|
||||
@@ -62,6 +63,13 @@ public static class MauiProgram
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
|
||||
ExplicitlyPreventThingsGetRemovedBecauseOfLinker();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void ExplicitlyPreventThingsGetRemovedBecauseOfLinker()
|
||||
{
|
||||
StubBaseCipherViewCellSoLinkerDoesntRemoveMethods.CallThisSoLinkerDoesntRemoveMethods();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,5 +38,11 @@ namespace Bit.App.Pages
|
||||
return _iconImageSource;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates if FFImageLoading has successfully finished loading the image.
|
||||
/// This is useful to check when the cell is being reused.
|
||||
/// </summary>
|
||||
public bool IconImageSuccesfullyLoaded { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user