mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 19:08:32 +03:00
PM-3349 PM-3350 MAUI Migration fix nullable exception bindings and AsyncCommand canExecute null exception
This commit is contained in:
parent
8ef9443b1e
commit
b8f0747dd4
15 changed files with 136 additions and 121 deletions
|
@ -16,7 +16,7 @@ namespace Bit.App.Controls
|
|||
FontFamily = "bwi-font";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "bwi-font.ttf#bwi-font";
|
||||
FontFamily = "bwi-font.ttf";
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Bit.App.Controls
|
|||
FontFamily = "bwi-font";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "bwi-font.ttf#bwi-font";
|
||||
FontFamily = "bwi-font.ttf";
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Bit.App.Controls
|
|||
FontFamily = "Material Icons";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "MaterialIcons_Regular.ttf#Material Icons";
|
||||
FontFamily = "MaterialIcons_Regular.ttf";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Bit.App.Controls
|
|||
FontFamily = "Material Icons";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "MaterialIcons_Regular.ttf#Material Icons";
|
||||
FontFamily = "MaterialIcons_Regular.ttf";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class MonoEntry : Entry
|
||||
{
|
||||
public MonoEntry()
|
||||
{
|
||||
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
switch (Device.RuntimePlatform)
|
||||
{
|
||||
case Device.iOS:
|
||||
FontFamily = "Menlo-Regular";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "RobotoMono_Regular.ttf#Roboto Mono";
|
||||
break;
|
||||
}
|
||||
#if ANDROID
|
||||
FontFamily = "RobotoMono_Regular";
|
||||
#elif IOS
|
||||
FontFamily = "Menlo-Regular";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Bit.App.Controls
|
|||
FontFamily = "Menlo-Regular";
|
||||
break;
|
||||
case Device.Android:
|
||||
FontFamily = "RobotoMono_Regular.ttf#Roboto Mono";
|
||||
FontFamily = "RobotoMono_Regular.ttf";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<!-- <controls:ExtendedToolbarItem
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||
|
@ -23,11 +23,11 @@
|
|||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Account}"
|
||||
AutomationId="AccountIconButton" /> -->
|
||||
AutomationId="AccountIconButton" />
|
||||
<ToolbarItem x:Name="_closeButton" Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"/>
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<!--<ContentPage.Resources>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="30" Padding="20, 50, 20, 0">
|
||||
|
@ -122,11 +122,9 @@
|
|||
</Label>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>-->
|
||||
</ContentPage.Resources>
|
||||
|
||||
<Label Text="Lalala" />
|
||||
|
||||
<!--<AbsoluteLayout
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
|
@ -142,5 +140,5 @@
|
|||
AbsoluteLayout.LayoutFlags="All"
|
||||
MainPage="{Binding Source={x:Reference _page}}"
|
||||
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||
</AbsoluteLayout>-->
|
||||
</AbsoluteLayout>
|
||||
</pages:BaseContentPage>
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Bit.App.Pages
|
|||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
// await _accountListOverlay.HideAsync();
|
||||
await _accountListOverlay.HideAsync();
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
UpdateLogo();
|
||||
|
@ -43,7 +43,7 @@ namespace Bit.App.Pages
|
|||
}
|
||||
if (_appOptions?.HideAccountSwitcher ?? false)
|
||||
{
|
||||
// ToolbarItems.Remove(_accountAvatar);
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,8 @@ namespace Bit.App.Pages
|
|||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
// _mainContent.Content = _mainLayout;
|
||||
// _accountAvatar?.OnAppearing();
|
||||
_mainContent.Content = _mainLayout;
|
||||
_accountAvatar?.OnAppearing();
|
||||
|
||||
if (!_appOptions?.HideAccountSwitcher ?? false)
|
||||
{
|
||||
|
@ -85,11 +85,11 @@ namespace Bit.App.Pages
|
|||
|
||||
protected override bool OnBackButtonPressed()
|
||||
{
|
||||
// if (_accountListOverlay.IsVisible)
|
||||
// {
|
||||
// _accountListOverlay.HideAsync().FireAndForget();
|
||||
// return true;
|
||||
// }
|
||||
if (_accountListOverlay.IsVisible)
|
||||
{
|
||||
_accountListOverlay.HideAsync().FireAndForget();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -97,12 +97,12 @@ namespace Bit.App.Pages
|
|||
{
|
||||
base.OnDisappearing();
|
||||
_broadcasterService.Unsubscribe(nameof(HomePage));
|
||||
// _accountAvatar?.OnDisappearing();
|
||||
_accountAvatar?.OnDisappearing();
|
||||
}
|
||||
|
||||
private void UpdateLogo()
|
||||
{
|
||||
// _logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||
}
|
||||
|
||||
private void Cancel_Clicked(object sender, EventArgs e)
|
||||
|
@ -141,7 +141,7 @@ namespace Bit.App.Pages
|
|||
|
||||
private async Task StartEnvironmentAsync()
|
||||
{
|
||||
// await _accountListOverlay.HideAsync();
|
||||
await _accountListOverlay.HideAsync();
|
||||
var page = new EnvironmentPage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
@ -345,7 +341,7 @@ namespace Bit.App.Pages
|
|||
get => _usernameOptions.PlusAddressedEmail;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.PlusAddressedEmail != value)
|
||||
if (_usernameOptions != null && _usernameOptions.PlusAddressedEmail != value)
|
||||
{
|
||||
_usernameOptions.PlusAddressedEmail = value;
|
||||
TriggerPropertyChanged(nameof(PlusAddressedEmail));
|
||||
|
@ -364,7 +360,7 @@ namespace Bit.App.Pages
|
|||
});
|
||||
}
|
||||
|
||||
public bool IsPolicyInEffect => _enforcedPolicyOptions.InEffect();
|
||||
public bool IsPolicyInEffect => _enforcedPolicyOptions?.InEffect() == true;
|
||||
|
||||
public GeneratorType GeneratorTypeSelected
|
||||
{
|
||||
|
@ -375,6 +371,7 @@ namespace Bit.App.Pages
|
|||
{
|
||||
IsUsername = value == GeneratorType.Username;
|
||||
TriggerPropertyChanged(nameof(GeneratorTypeSelected));
|
||||
TriggerPropertyChanged(nameof(UsernameTypeSelected));
|
||||
SaveOptionsAsync().FireAndForget();
|
||||
SaveUsernameOptionsAsync().FireAndForget();
|
||||
}
|
||||
|
@ -398,10 +395,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public UsernameType UsernameTypeSelected
|
||||
{
|
||||
get => _usernameOptions.Type;
|
||||
get => _usernameOptions?.Type ?? UsernameType.PlusAddressedEmail;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.Type != value)
|
||||
if (_usernameOptions != null && _usernameOptions.Type != value)
|
||||
{
|
||||
_usernameOptions.Type = value;
|
||||
Username = Constants.DefaultUsernameGenerated;
|
||||
|
@ -415,10 +412,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public ForwardedEmailServiceType ForwardedEmailServiceSelected
|
||||
{
|
||||
get => _usernameOptions.ServiceType;
|
||||
get => _usernameOptions?.ServiceType ?? ForwardedEmailServiceType.None;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.ServiceType != value)
|
||||
if (_usernameOptions != null && _usernameOptions.ServiceType != value)
|
||||
{
|
||||
_usernameOptions.ServiceType = value;
|
||||
Username = Constants.DefaultUsernameGenerated;
|
||||
|
@ -434,10 +431,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public string CatchAllEmailDomain
|
||||
{
|
||||
get => _usernameOptions.CatchAllEmailDomain;
|
||||
get => _usernameOptions?.CatchAllEmailDomain;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.CatchAllEmailDomain != value)
|
||||
if (_usernameOptions != null && _usernameOptions.CatchAllEmailDomain != value)
|
||||
{
|
||||
_usernameOptions.CatchAllEmailDomain = value;
|
||||
TriggerPropertyChanged(nameof(CatchAllEmailDomain));
|
||||
|
@ -450,6 +447,11 @@ namespace Bit.App.Pages
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_usernameOptions is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (ForwardedEmailServiceSelected)
|
||||
{
|
||||
case ForwardedEmailServiceType.AnonAddy:
|
||||
|
@ -468,6 +470,11 @@ namespace Bit.App.Pages
|
|||
}
|
||||
set
|
||||
{
|
||||
if (_usernameOptions is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
switch (ForwardedEmailServiceSelected)
|
||||
{
|
||||
|
@ -548,10 +555,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public string AddyIoDomainName
|
||||
{
|
||||
get => _usernameOptions.AnonAddyDomainName;
|
||||
get => _usernameOptions?.AnonAddyDomainName;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.AnonAddyDomainName != value)
|
||||
if (_usernameOptions != null && _usernameOptions.AnonAddyDomainName != value)
|
||||
{
|
||||
_usernameOptions.AnonAddyDomainName = value;
|
||||
TriggerPropertyChanged(nameof(AddyIoDomainName));
|
||||
|
@ -562,10 +569,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public bool CapitalizeRandomWordUsername
|
||||
{
|
||||
get => _usernameOptions.CapitalizeRandomWordUsername;
|
||||
get => _usernameOptions?.CapitalizeRandomWordUsername == true;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.CapitalizeRandomWordUsername != value)
|
||||
if (_usernameOptions != null && _usernameOptions.CapitalizeRandomWordUsername != value)
|
||||
{
|
||||
_usernameOptions.CapitalizeRandomWordUsername = value;
|
||||
TriggerPropertyChanged(nameof(CapitalizeRandomWordUsername));
|
||||
|
@ -576,10 +583,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public bool IncludeNumberRandomWordUsername
|
||||
{
|
||||
get => _usernameOptions.IncludeNumberRandomWordUsername;
|
||||
get => _usernameOptions?.IncludeNumberRandomWordUsername == true;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.IncludeNumberRandomWordUsername != value)
|
||||
if (_usernameOptions != null && _usernameOptions.IncludeNumberRandomWordUsername != value)
|
||||
{
|
||||
_usernameOptions.IncludeNumberRandomWordUsername = value;
|
||||
TriggerPropertyChanged(nameof(IncludeNumberRandomWordUsername));
|
||||
|
@ -590,10 +597,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public UsernameEmailType PlusAddressedEmailTypeSelected
|
||||
{
|
||||
get => _usernameOptions.PlusAddressedEmailType;
|
||||
get => _usernameOptions?.PlusAddressedEmailType ?? UsernameEmailType.Random;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.PlusAddressedEmailType != value)
|
||||
if (_usernameOptions != null && _usernameOptions.PlusAddressedEmailType != value)
|
||||
{
|
||||
_usernameOptions.PlusAddressedEmailType = value;
|
||||
TriggerPropertyChanged(nameof(PlusAddressedEmailTypeSelected));
|
||||
|
@ -604,10 +611,10 @@ namespace Bit.App.Pages
|
|||
|
||||
public UsernameEmailType CatchAllEmailTypeSelected
|
||||
{
|
||||
get => _usernameOptions.CatchAllEmailType;
|
||||
get => _usernameOptions?.CatchAllEmailType ?? UsernameEmailType.Random;
|
||||
set
|
||||
{
|
||||
if (_usernameOptions.CatchAllEmailType != value)
|
||||
if (_usernameOptions != null && _usernameOptions.CatchAllEmailType != value)
|
||||
{
|
||||
_usernameOptions.CatchAllEmailType = value;
|
||||
TriggerPropertyChanged(nameof(CatchAllEmailTypeSelected));
|
||||
|
|
|
@ -258,10 +258,11 @@
|
|||
<StackLayout.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding ToggleOptionsCommand}" />
|
||||
</StackLayout.GestureRecognizers>
|
||||
<Button
|
||||
<controls:CustomLabel
|
||||
Text="{u:I18n Options}"
|
||||
x:Name="_btnOptions"
|
||||
StyleClass="box-row-button"
|
||||
VerticalTextAlignment="Center"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
Margin="0"
|
||||
AutomationProperties.IsInAccessibleTree="False"
|
||||
|
@ -408,7 +409,7 @@
|
|||
AutomationId="SendMaxAccessCountEntry" />
|
||||
<controls:ExtendedStepper
|
||||
x:Name="_maxAccessCountStepper"
|
||||
Value="{Binding MaxAccessCount}"
|
||||
Value="{Binding MaxAccessCount, TargetNullValue=0}"
|
||||
Maximum="999999999"
|
||||
IsEnabled="{Binding SendEnabled}"
|
||||
Margin="10,0,0,0"
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
StyleClass="box-label"/>
|
||||
<Entry
|
||||
x:Name="_loginUsernameEntry"
|
||||
Text="{Binding Cipher.Login.Username}"
|
||||
Text="{Binding Cipher.Login.Username, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
|
@ -175,7 +175,7 @@
|
|||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_loginPasswordEntry"
|
||||
Text="{Binding Cipher.Login.Password}"
|
||||
Text="{Binding Cipher.Login.Password, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
|
@ -183,7 +183,7 @@
|
|||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsEnabled="{Binding Cipher.ViewPassword}"
|
||||
IsEnabled="{Binding Cipher.ViewPassword, FallbackValue=False}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Password}"
|
||||
AutomationId="LoginPasswordEntry" />
|
||||
|
@ -196,7 +196,7 @@
|
|||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n CheckPassword}"
|
||||
IsVisible="{Binding Cipher.ViewPassword}"
|
||||
IsVisible="{Binding Cipher.ViewPassword, FallbackValue=False}"
|
||||
AutomationId="CheckPasswordButton" />
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
|
@ -208,7 +208,7 @@
|
|||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
||||
IsVisible="{Binding Cipher.ViewPassword}"
|
||||
IsVisible="{Binding Cipher.ViewPassword, FallbackValue=False}"
|
||||
AutomationId="ViewPasswordButton" />
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
|
@ -237,7 +237,7 @@
|
|||
<Grid StyleClass="box-row, box-row-input">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
|
@ -253,8 +253,6 @@
|
|||
IsVisible="{Binding HasTotpValue, Converter={StaticResource inverseBool}}"
|
||||
Margin="0,5,0,0"
|
||||
StyleClass="btn-icon-row"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
Padding="0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
|
@ -272,12 +270,12 @@
|
|||
</Frame>
|
||||
<controls:MonoEntry
|
||||
x:Name="_loginTotpEntry"
|
||||
Text="{Binding Cipher.Login.Totp}"
|
||||
Text="{Binding Cipher.Login.Totp, FallbackValue=''}"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsVisible="{Binding HasTotpValue}"
|
||||
IsPassword="{Binding Cipher.ViewPassword, Converter={StaticResource inverseBool}}"
|
||||
IsEnabled="{Binding Cipher.ViewPassword}"
|
||||
IsPassword="{Binding Cipher.ViewPassword, Converter={StaticResource inverseBool}, FallbackValue=False}"
|
||||
IsEnabled="{Binding Cipher.ViewPassword, FallbackValue=False}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
|
@ -316,7 +314,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_cardholderNameEntry"
|
||||
Text="{Binding Cipher.Card.CardholderName}"
|
||||
Text="{Binding Cipher.Card.CardholderName, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationId="CardholderNameEntry" />
|
||||
</StackLayout>
|
||||
|
@ -336,7 +334,7 @@
|
|||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_cardNumberEntry"
|
||||
Text="{Binding Cipher.Card.Number}"
|
||||
Text="{Binding Cipher.Card.Number, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
|
@ -385,7 +383,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_cardExpYearEntry"
|
||||
Text="{Binding Cipher.Card.ExpYear}"
|
||||
Text="{Binding Cipher.Card.ExpYear, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
Keyboard="Numeric"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
|
@ -408,7 +406,7 @@
|
|||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_cardCodeEntry"
|
||||
Text="{Binding Cipher.Card.Code}"
|
||||
Text="{Binding Cipher.Card.Code, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
|
@ -449,7 +447,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityFirstNameEntry"
|
||||
Text="{Binding Cipher.Identity.FirstName}"
|
||||
Text="{Binding Cipher.Identity.FirstName, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-word-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n FirstName}"
|
||||
|
@ -461,7 +459,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityMiddleNameEntry"
|
||||
Text="{Binding Cipher.Identity.MiddleName}"
|
||||
Text="{Binding Cipher.Identity.MiddleName, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-word-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n MiddleName}"
|
||||
|
@ -473,7 +471,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityLastNameEntry"
|
||||
Text="{Binding Cipher.Identity.LastName}"
|
||||
Text="{Binding Cipher.Identity.LastName, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-word-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n LastName}"
|
||||
|
@ -485,7 +483,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityUsernameEntry"
|
||||
Text="{Binding Cipher.Identity.Username}"
|
||||
Text="{Binding Cipher.Identity.Username, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Username}"
|
||||
|
@ -497,7 +495,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityCompanyEntry"
|
||||
Text="{Binding Cipher.Identity.Company}"
|
||||
Text="{Binding Cipher.Identity.Company, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Company}"
|
||||
|
@ -509,7 +507,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identitySsnEntry"
|
||||
Text="{Binding Cipher.Identity.SSN}"
|
||||
Text="{Binding Cipher.Identity.SSN, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n SSN}"
|
||||
|
@ -521,7 +519,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityPassportNumberEntry"
|
||||
Text="{Binding Cipher.Identity.PassportNumber}"
|
||||
Text="{Binding Cipher.Identity.PassportNumber, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n PassportNumber}"
|
||||
|
@ -533,7 +531,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityLicenseNumberEntry"
|
||||
Text="{Binding Cipher.Identity.LicenseNumber}"
|
||||
Text="{Binding Cipher.Identity.LicenseNumber, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n LicenseNumber}"
|
||||
|
@ -546,7 +544,7 @@
|
|||
<Entry
|
||||
x:Name="_identityEmailEntry"
|
||||
Keyboard="Email"
|
||||
Text="{Binding Cipher.Identity.Email}"
|
||||
Text="{Binding Cipher.Identity.Email, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Email}"
|
||||
|
@ -558,7 +556,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityPhoneEntry"
|
||||
Text="{Binding Cipher.Identity.Phone}"
|
||||
Text="{Binding Cipher.Identity.Phone, FallbackValue=''}"
|
||||
Keyboard="Telephone"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
|
@ -571,7 +569,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityAddress1Entry"
|
||||
Text="{Binding Cipher.Identity.Address1}"
|
||||
Text="{Binding Cipher.Identity.Address1, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Address1}"
|
||||
|
@ -583,7 +581,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityAddress2Entry"
|
||||
Text="{Binding Cipher.Identity.Address2}"
|
||||
Text="{Binding Cipher.Identity.Address2, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Address2}"
|
||||
|
@ -595,7 +593,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityAddress3Entry"
|
||||
Text="{Binding Cipher.Identity.Address3}"
|
||||
Text="{Binding Cipher.Identity.Address3, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Address3}"
|
||||
|
@ -607,7 +605,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityCityEntry"
|
||||
Text="{Binding Cipher.Identity.City}"
|
||||
Text="{Binding Cipher.Identity.City, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-sentence-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n CityTown}"
|
||||
|
@ -619,7 +617,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityStateEntry"
|
||||
Text="{Binding Cipher.Identity.State}"
|
||||
Text="{Binding Cipher.Identity.State, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-sentence-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n StateProvince}"
|
||||
|
@ -631,7 +629,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityPostalCodeEntry"
|
||||
Text="{Binding Cipher.Identity.PostalCode}"
|
||||
Text="{Binding Cipher.Identity.PostalCode, FallbackValue=''}"
|
||||
StyleClass="box-value"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n ZipPostalCode}"
|
||||
|
@ -643,7 +641,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_identityCountryEntry"
|
||||
Text="{Binding Cipher.Identity.Country}"
|
||||
Text="{Binding Cipher.Identity.Country, FallbackValue=''}"
|
||||
StyleClass="box-value,capitalize-sentence-input"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Country}"
|
||||
|
@ -656,13 +654,13 @@
|
|||
<Label Text="{u:I18n URIs, Header=True}"
|
||||
StyleClass="box-header, box-header-platform" />
|
||||
</StackLayout>
|
||||
<controls:RepeaterView ItemsSource="{Binding Uris}">
|
||||
<controls:RepeaterView.ItemTemplate>
|
||||
<StackLayout BindableLayout.ItemsSource="{Binding Uris}">
|
||||
<BindableLayout.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:LoginUriView">
|
||||
<Grid StyleClass="box-row, box-row-input" AutomationId="UriListGrid" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
|
@ -695,8 +693,8 @@
|
|||
AutomationId="LoginUriOptionsButton" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</controls:RepeaterView.ItemTemplate>
|
||||
</controls:RepeaterView>
|
||||
</BindableLayout.ItemTemplate>
|
||||
</StackLayout>
|
||||
<Button Text="{u:I18n NewUri}" StyleClass="box-button-row"
|
||||
Clicked="NewUri_Clicked"
|
||||
AutomationId="LoginAddNewUriButton"></Button>
|
||||
|
@ -723,7 +721,7 @@
|
|||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding Cipher.Favorite}"
|
||||
IsToggled="{Binding Cipher.Favorite, FallbackValue=False}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationId="ItemFavoriteToggle" />
|
||||
|
@ -741,7 +739,7 @@
|
|||
SemanticProperties.Description="{u:I18n MasterPasswordRePromptHelp}"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding PasswordPrompt}"
|
||||
IsToggled="{Binding PasswordPrompt, Mode=OneWay}"
|
||||
Toggled="PasswordPrompt_Toggled"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
|
@ -760,7 +758,7 @@
|
|||
AutoSize="TextChanges"
|
||||
StyleClass="box-value"
|
||||
effects:ScrollEnabledEffect.IsScrollEnabled="false"
|
||||
Text="{Binding Cipher.Notes}"
|
||||
Text="{Binding Cipher.Notes, FallbackValue=''}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
SemanticProperties.Description="{u:I18n Notes}"
|
||||
AutomationId="ItemNotesEntry">
|
||||
|
@ -799,7 +797,7 @@
|
|||
StyleClass="box-label" />
|
||||
<Picker
|
||||
x:Name="_ownershipPicker"
|
||||
ItemsSource="{Binding OwnershipOptions, Mode=OneTime}"
|
||||
ItemsSource="{Binding OwnershipOptions, Mode=OneWay}"
|
||||
SelectedIndex="{Binding OwnershipSelectedIndex}"
|
||||
StyleClass="box-value"
|
||||
AutomationId="ItemOwnershipPicker" />
|
||||
|
|
|
@ -288,7 +288,7 @@ namespace Bit.App.Pages
|
|||
nameof(ShowCollections)
|
||||
});
|
||||
}
|
||||
public bool ShowCollections => (!EditMode || CloneMode) && Cipher.OrganizationId != null;
|
||||
public bool ShowCollections => (!EditMode || CloneMode) && Cipher?.OrganizationId != null;
|
||||
public bool EditMode => !string.IsNullOrWhiteSpace(CipherId);
|
||||
public bool ShowOwnershipOptions => !EditMode || CloneMode;
|
||||
public bool OwnershipPolicyInEffect => ShowOwnershipOptions && !AllowPersonal;
|
||||
|
@ -298,15 +298,15 @@ namespace Bit.App.Pages
|
|||
public bool IsIdentity => Cipher?.Type == CipherType.Identity;
|
||||
public bool IsCard => Cipher?.Type == CipherType.Card;
|
||||
public bool IsSecureNote => Cipher?.Type == CipherType.SecureNote;
|
||||
public bool ShowUris => IsLogin && Cipher.Login.HasUris;
|
||||
public bool ShowAttachments => Cipher.HasAttachments;
|
||||
public bool ShowUris => IsLogin && Cipher != null && Cipher.Login.HasUris;
|
||||
public bool ShowAttachments => Cipher != null && Cipher.HasAttachments;
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4;
|
||||
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
||||
public int PasswordFieldColSpan => Cipher != null && Cipher.ViewPassword ? 1 : 4;
|
||||
public int TotpColumnSpan => Cipher != null && Cipher.ViewPassword ? 1 : 2;
|
||||
public bool AllowPersonal { get; set; }
|
||||
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
|
||||
public bool PasswordPrompt => Cipher != null && Cipher.Reprompt != CipherRepromptType.None;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
|
||||
public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
|
||||
|
@ -459,6 +459,12 @@ namespace Bit.App.Pages
|
|||
}
|
||||
_previousCipherId = CipherId;
|
||||
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
TriggerPropertyChanged(nameof(OwnershipOptions));
|
||||
TriggerPropertyChanged(nameof(OwnershipSelectedIndex));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -892,7 +898,7 @@ namespace Bit.App.Pages
|
|||
|
||||
private void TriggerCipherChanged()
|
||||
{
|
||||
TriggerPropertyChanged(nameof(Cipher), AdditionalPropertiesToRaiseOnCipherChanged);
|
||||
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(Cipher), AdditionalPropertiesToRaiseOnCipherChanged));
|
||||
}
|
||||
|
||||
private async Task CopyTotpClipboardAsync()
|
||||
|
|
|
@ -198,11 +198,14 @@ namespace Bit.App.Pages
|
|||
Text = string.Format("{0}:", AppResources.PasswordHistory),
|
||||
FontAttributes = FontAttributes.Bold
|
||||
});
|
||||
fs.Spans.Add(new Span
|
||||
if (Cipher?.PasswordHistory != null)
|
||||
{
|
||||
Text = string.Format(" {0}", Cipher.PasswordHistory.Count.ToString()),
|
||||
TextColor = ThemeManager.GetResourceColor("PrimaryColor")
|
||||
});
|
||||
fs.Spans.Add(new Span
|
||||
{
|
||||
Text = string.Format(" {0}", Cipher.PasswordHistory.Count.ToString()),
|
||||
TextColor = ThemeManager.GetResourceColor("PrimaryColor")
|
||||
});
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,12 @@ namespace Bit.App.Utilities
|
|||
}
|
||||
}
|
||||
|
||||
_relayCommand = new AsyncRelayCommand(doAsync, canExecute, allowsMultipleExecutions ? AsyncRelayCommandOptions.AllowConcurrentExecutions : AsyncRelayCommandOptions.None);
|
||||
var safeCanExecute = canExecute;
|
||||
if (canExecute is null)
|
||||
{
|
||||
safeCanExecute = () => true;
|
||||
}
|
||||
_relayCommand = new AsyncRelayCommand(doAsync, safeCanExecute, allowsMultipleExecutions ? AsyncRelayCommandOptions.AllowConcurrentExecutions : AsyncRelayCommandOptions.None);
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
@ -58,7 +63,12 @@ namespace Bit.App.Utilities
|
|||
}
|
||||
}
|
||||
|
||||
_relayCommand = new AsyncRelayCommand<T>(doAsync, canExecute, allowsMultipleExecutions ? AsyncRelayCommandOptions.AllowConcurrentExecutions : AsyncRelayCommandOptions.None);
|
||||
var safeCanExecute = canExecute;
|
||||
if (canExecute is null)
|
||||
{
|
||||
safeCanExecute = _ => true;
|
||||
}
|
||||
_relayCommand = new AsyncRelayCommand<T>(doAsync, safeCanExecute, allowsMultipleExecutions ? AsyncRelayCommandOptions.AllowConcurrentExecutions : AsyncRelayCommandOptions.None);
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Bit.App.Utilities
|
|||
public object Convert(object value, Type targetType, object parameter,
|
||||
System.Globalization.CultureInfo culture)
|
||||
{
|
||||
return EnumHelper.GetLocalizedValue(value, value.GetType());
|
||||
return value != null ? EnumHelper.GetLocalizedValue(value, value.GetType()) : string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter,
|
||||
|
|
Loading…
Reference in a new issue