mirror of
https://github.com/bitwarden/mobile
synced 2026-01-07 19:13:19 +00:00
Merge branch 'feature/maui-migration' into feature/maui-migration-passkeys
This commit is contained in:
49
.github/workflows/build.yml
vendored
49
.github/workflows/build.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@031090342aeefe171e49f3820f3b52110c66e402 # v1.3.2
|
||||
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
|
||||
|
||||
# This step might be obsolete at some point as .NET MAUI workloads
|
||||
# are starting to come pre-installed on the GH Actions build agents.
|
||||
@@ -310,7 +310,7 @@ jobs:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@031090342aeefe171e49f3820f3b52110c66e402 # v1.3.2
|
||||
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
|
||||
|
||||
# This step might be obsolete at some point as .NET MAUI workloads
|
||||
# are starting to come pre-installed on the GH Actions build agents.
|
||||
@@ -366,13 +366,6 @@ jobs:
|
||||
|
||||
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");
|
||||
|
||||
# Write-Output "########################################"
|
||||
# Write-Output "##### Clean Android and App"
|
||||
# Write-Output "########################################"
|
||||
|
||||
# msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||
# msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||
|
||||
Write-Output "########################################"
|
||||
Write-Output "##### Backup project files"
|
||||
Write-Output "########################################"
|
||||
@@ -392,42 +385,6 @@ jobs:
|
||||
|
||||
$xml.Save($androidManifest);
|
||||
|
||||
# Write-Output "########################################"
|
||||
# Write-Output "##### Uninstall from App.csproj"
|
||||
# Write-Output "########################################"
|
||||
|
||||
# $xml=New-Object XML;
|
||||
# $xml.Load($appPath);
|
||||
|
||||
# $ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||
# $ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
|
||||
|
||||
# $firebaseNode=$xml.SelectSingleNode(`
|
||||
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
||||
# $firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
||||
|
||||
# $daggerNode=$xml.SelectSingleNode(`
|
||||
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
|
||||
# $daggerNode.ParentNode.RemoveChild($daggerNode);
|
||||
|
||||
# $safetyNetNode=$xml.SelectSingleNode(`
|
||||
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
||||
# $safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
||||
|
||||
# $xml.Save($appPath);
|
||||
|
||||
# Write-Output "########################################"
|
||||
# Write-Output "##### Uninstall from Core.csproj"
|
||||
# Write-Output "########################################"
|
||||
|
||||
# $xml=New-Object XML;
|
||||
# $xml.Load($corePath);
|
||||
|
||||
# $appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||
# $appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||
|
||||
# $xml.Save($corePath);
|
||||
|
||||
- name: Restore packages
|
||||
run: dotnet restore
|
||||
|
||||
@@ -811,7 +768,7 @@ jobs:
|
||||
secrets: "crowdin-api-token"
|
||||
|
||||
- name: Upload Sources
|
||||
uses: crowdin/github-action@6fb7e99759b996fd142995636fd8c88c1fb8ecd9 # v1.16.1
|
||||
uses: crowdin/github-action@97bef4fd3f1b853eb105bc99b8d0d563760e024c # v1.17.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||
|
||||
2
.github/workflows/crowdin-pull.yml
vendored
2
.github/workflows/crowdin-pull.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
||||
|
||||
- name: Download translations
|
||||
uses: crowdin/github-action@6fb7e99759b996fd142995636fd8c88c1fb8ecd9 # v1.16.1
|
||||
uses: crowdin/github-action@97bef4fd3f1b853eb105bc99b8d0d563760e024c # v1.17.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
|
||||
- name: Download all artifacts
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0
|
||||
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
|
||||
- name: Dry Run - Download all artifacts
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0
|
||||
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
|
||||
- name: Download F-Droid .apk artifact
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0
|
||||
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
|
||||
- name: Dry Run - Download F-Droid .apk artifact
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0
|
||||
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
|
||||
35
.github/workflows/version-bump.yml
vendored
35
.github/workflows/version-bump.yml
vendored
@@ -36,10 +36,19 @@ jobs:
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: main
|
||||
repository: bitwarden/mobile
|
||||
|
||||
- name: Check if RC branch exists
|
||||
if: ${{ inputs.cut_rc_branch == true }}
|
||||
run: |
|
||||
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
||||
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
||||
echo "Remote RC branch exists."
|
||||
echo "Please delete current RC branch before running again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@d6f3f49f3345e29369fe57596a3ca8f94c4d2ca7 # v5.4.0
|
||||
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
||||
with:
|
||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||
@@ -183,14 +192,22 @@ jobs:
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Check if RC branch exists
|
||||
- name: Verify version has been updated
|
||||
env:
|
||||
NEW_VERSION: ${{ inputs.version_number }}
|
||||
run: |
|
||||
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
||||
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
||||
echo "Remote RC branch exists."
|
||||
echo "Please delete current RC branch before running again."
|
||||
exit 1
|
||||
fi
|
||||
CURRENT_VERSION=$(xmllint --xpath '
|
||||
string(/manifest/@*[local-name()="versionName"
|
||||
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
||||
' src/Android/Properties/AndroidManifest.xml)
|
||||
|
||||
# Wait for version to change.
|
||||
while [[ "$NEW_VERSION" != "$CURRENT_VERSION" ]]
|
||||
do
|
||||
echo "Waiting for version to be updated..."
|
||||
sleep 10
|
||||
git pull --force
|
||||
done
|
||||
|
||||
- name: Cut RC branch
|
||||
run: |
|
||||
|
||||
@@ -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