mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 00:03:22 +00:00
got the countdown working
This commit is contained in:
@@ -3,35 +3,92 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Controls.AuthenticatorViewCell"
|
x:Class="Bit.App.Controls.AuthenticatorViewCell"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
|
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
|
||||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
||||||
StyleClass="list-row, list-row-platform"
|
StyleClass="list-row, list-row-platform"
|
||||||
RowSpacing="0"
|
RowSpacing="0"
|
||||||
ColumnSpacing="0"
|
ColumnSpacing="0"
|
||||||
x:DataType="controls:AuthenticatorViewCellViewModel">
|
x:DataType="pages:AuthenticatorPageListItem">
|
||||||
|
|
||||||
|
<Grid.Resources>
|
||||||
|
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
||||||
|
<u:IconImageConverter x:Key="iconImageConverter"/>
|
||||||
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
|
||||||
|
</Grid.Resources>
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="40" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="60" />
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Label
|
<controls:IconLabel
|
||||||
Text="Figma"
|
Grid.Column="0"
|
||||||
StyleClass="box-label"
|
HorizontalOptions="Center"
|
||||||
Grid.Row="0"
|
VerticalOptions="Center"
|
||||||
Grid.Column="0" />
|
StyleClass="list-icon, list-icon-platform"
|
||||||
<controls:MonoLabel
|
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||||
Text="{Binding TotpCodeFormatted, Mode=OneWay}"
|
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||||
StyleClass="box-value"
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0" />
|
<ff:CachedImage
|
||||||
|
Grid.Column="0"
|
||||||
|
BitmapOptimizations="True"
|
||||||
|
ErrorPlaceholder="login.png"
|
||||||
|
LoadingPlaceholder="login.png"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
WidthRequest="22"
|
||||||
|
HeightRequest="22"
|
||||||
|
IsVisible="{Binding ShowIconImage}"
|
||||||
|
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<controls:MonoLabel
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
StyleClass="list-title, list-title-platform"
|
||||||
|
Text="{Binding TotpCodeFormatted, Mode=OneWay}" />
|
||||||
|
<Label
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="1"
|
||||||
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
|
Text="{Binding Cipher.Name}" />
|
||||||
|
<controls:IconLabel
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="1"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text="{Binding Source={x:Static core:BitwardenIcons.Collection}}"
|
||||||
|
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Shared}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
Text="{Binding TotpSec, Mode=OneWay}"
|
Text="{Binding TotpSec, Mode=OneWay}"
|
||||||
Style="{DynamicResource textTotp}"
|
Style="{DynamicResource textTotp}"
|
||||||
@@ -50,6 +107,7 @@
|
|||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
|
Padding="0,0,1,0"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n CopyTotp}" />
|
AutomationProperties.Name="{u:I18n CopyTotp}" />
|
||||||
</controls:ExtendedGrid>
|
</controls:ExtendedGrid>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
@@ -7,64 +9,112 @@ namespace Bit.App.Controls
|
|||||||
public partial class AuthenticatorViewCell : ExtendedGrid
|
public partial class AuthenticatorViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
||||||
nameof(Cipher), typeof(CipherView), typeof(AuthenticatorViewCell), default(CipherView), BindingMode.OneWay);
|
nameof(Cipher), typeof(CipherView), typeof(AuthenticatorViewCell), default(CipherView), BindingMode.TwoWay);
|
||||||
|
|
||||||
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
|
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
|
||||||
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(AuthenticatorViewCell));
|
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(AuthenticatorViewCell));
|
||||||
|
|
||||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
public static readonly BindableProperty TotpSecProperty = BindableProperty.Create(
|
||||||
nameof(ButtonCommand), typeof(Command<CipherView>), typeof(AuthenticatorViewCell));
|
nameof(TotpSec), typeof(long), typeof(AuthenticatorViewCell));
|
||||||
|
|
||||||
|
//public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||||
|
// nameof(ButtonCommand), typeof(Command<CipherView>), typeof(AuthenticatorViewCell));
|
||||||
|
|
||||||
public AuthenticatorViewCell()
|
public AuthenticatorViewCell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Command CopyCommand { get; set; }
|
||||||
|
|
||||||
|
public CipherView Cipher
|
||||||
|
{
|
||||||
|
get => GetValue(CipherProperty) as CipherView;
|
||||||
|
set => SetValue(CipherProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public bool? WebsiteIconsEnabled
|
public bool? WebsiteIconsEnabled
|
||||||
{
|
{
|
||||||
get => (bool)GetValue(WebsiteIconsEnabledProperty);
|
get => (bool)GetValue(WebsiteIconsEnabledProperty);
|
||||||
set => SetValue(WebsiteIconsEnabledProperty, value);
|
set => SetValue(WebsiteIconsEnabledProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CipherView Cipher
|
public long TotpSec
|
||||||
{
|
{
|
||||||
get => GetValue(CipherProperty) as CipherView;
|
get => (long)GetValue(TotpSecProperty);
|
||||||
set => SetValue(CipherProperty, value);
|
set => SetValue(TotpSecProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command<CipherView> ButtonCommand
|
public bool ShowIconImage
|
||||||
{
|
{
|
||||||
get => GetValue(ButtonCommandProperty) as Command<CipherView>;
|
get => WebsiteIconsEnabled ?? false
|
||||||
set => SetValue(ButtonCommandProperty, value);
|
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
|
||||||
|
&& IconImageSource != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(string propertyName = null)
|
private string _iconImageSource = string.Empty;
|
||||||
|
public string IconImageSource
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(propertyName);
|
get
|
||||||
if (propertyName == CipherProperty.PropertyName)
|
|
||||||
{
|
{
|
||||||
if (Cipher == null)
|
if (_iconImageSource == string.Empty) // default value since icon source can return null
|
||||||
{
|
{
|
||||||
return;
|
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
|
||||||
}
|
}
|
||||||
BindingContext = new AuthenticatorViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
|
return _iconImageSource;
|
||||||
}
|
|
||||||
else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
|
|
||||||
{
|
|
||||||
if (Cipher == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((AuthenticatorViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _totpCodeFormatted = "938 928";
|
||||||
|
public string TotpCodeFormatted
|
||||||
|
{
|
||||||
|
get => _totpCodeFormatted;
|
||||||
|
set => _totpCodeFormatted = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public Command<CipherView> ButtonCommand
|
||||||
|
//{
|
||||||
|
// get => GetValue(ButtonCommandProperty) as Command<CipherView>;
|
||||||
|
// set => SetValue(ButtonCommandProperty, value);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//protected override void OnPropertyChanged(string propertyName = null)
|
||||||
|
//{
|
||||||
|
// base.OnPropertyChanged(propertyName);
|
||||||
|
// if (propertyName == CipherProperty.PropertyName)
|
||||||
|
// {
|
||||||
|
// if (Cipher == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// _cipherLabel.Text = Cipher.Name;
|
||||||
|
// }
|
||||||
|
// else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
|
||||||
|
// {
|
||||||
|
// if (Cipher == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// ((AuthenticatorViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
|
||||||
|
// }
|
||||||
|
// else if (propertyName == TotpSecProperty.PropertyName)
|
||||||
|
// {
|
||||||
|
// if (Cipher == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// ((AuthenticatorViewCellViewModel)BindingContext).UpdateTotpSec(TotpSec);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
private void MoreButton_Clicked(object sender, EventArgs e)
|
private void MoreButton_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var cipher = ((sender as MiButton)?.BindingContext as AuthenticatorViewCellViewModel)?.Cipher;
|
var cipher = ((sender as MiButton)?.BindingContext as AuthenticatorViewCellViewModel)?.Cipher;
|
||||||
if (cipher != null)
|
if (cipher != null)
|
||||||
{
|
{
|
||||||
ButtonCommand?.Execute(cipher);
|
//ButtonCommand?.Execute(cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -8,7 +9,7 @@ namespace Bit.App.Controls
|
|||||||
public class AuthenticatorViewCellViewModel : ExtendedViewModel
|
public class AuthenticatorViewCellViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private string _totpCodeFormatted = "938928";
|
private string _totpCodeFormatted = "938 928";
|
||||||
private string _totpSec;
|
private string _totpSec;
|
||||||
private bool _websiteIconsEnabled;
|
private bool _websiteIconsEnabled;
|
||||||
private string _iconImageSource = string.Empty;
|
private string _iconImageSource = string.Empty;
|
||||||
@@ -64,6 +65,39 @@ namespace Bit.App.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateTotpSec(long totpSec)
|
||||||
|
{
|
||||||
|
_totpSec = totpSec.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//private async Task TotpUpdateCodeAsync()
|
||||||
|
//{
|
||||||
|
// if (Cipher == null || Cipher.Type != Core.Enums.CipherType.Login || Cipher.Login.Totp == null)
|
||||||
|
// {
|
||||||
|
// _totpInterval = null;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// _totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
|
||||||
|
// if (_totpCode != null)
|
||||||
|
// {
|
||||||
|
// if (_totpCode.Length > 4)
|
||||||
|
// {
|
||||||
|
// var half = (int)Math.Floor(_totpCode.Length / 2M);
|
||||||
|
// TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
|
||||||
|
// _totpCode.Substring(half));
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// TotpCodeFormatted = _totpCode;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// TotpCodeFormatted = null;
|
||||||
|
// _totpInterval = null;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
x:DataType="pages:AuthenticatorPageViewModel"
|
x:DataType="pages:AuthenticatorPageViewModel"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}"
|
||||||
|
x:Name="_page">
|
||||||
|
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:AuthenticatorPageViewModel />
|
<pages:AuthenticatorPageViewModel />
|
||||||
@@ -40,31 +41,31 @@
|
|||||||
AutomationProperties.Name="{u:I18n AddItem}" />
|
AutomationProperties.Name="{u:I18n AddItem}" />
|
||||||
|
|
||||||
<DataTemplate x:Key="authenticatorTemplate"
|
<DataTemplate x:Key="authenticatorTemplate"
|
||||||
x:DataType="pages:SendGroupingsPageListItem">
|
x:DataType="pages:AuthenticatorPageListItem">
|
||||||
<controls:AuthenticatorViewCell />
|
<controls:AuthenticatorViewCell
|
||||||
|
Cipher="{Binding Cipher}"
|
||||||
|
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
|
||||||
|
TotpSec="{Binding TotpSec}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
|
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
|
||||||
<RefreshView
|
<RefreshView>
|
||||||
IsVisible="{Binding ShowList}"
|
|
||||||
IsRefreshing="{Binding Refreshing}"
|
|
||||||
Command="{Binding RefreshCommand}">
|
|
||||||
<controls:ExtendedCollectionView
|
<controls:ExtendedCollectionView
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
ItemTemplate="{StaticResource authenticatorTemplate}"
|
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
|
|
||||||
|
<controls:ExtendedCollectionView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="pages:AuthenticatorPageListItem">
|
||||||
|
<controls:AuthenticatorViewCell />
|
||||||
|
</DataTemplate>
|
||||||
|
</controls:ExtendedCollectionView.ItemTemplate>
|
||||||
|
|
||||||
</controls:ExtendedCollectionView>
|
</controls:ExtendedCollectionView>
|
||||||
</RefreshView>
|
</RefreshView>
|
||||||
|
|
||||||
<StackLayout
|
|
||||||
IsVisible="{Binding ShowList}">
|
|
||||||
|
|
||||||
</StackLayout>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -14,6 +15,8 @@ namespace Bit.App.Pages
|
|||||||
#region Members
|
#region Members
|
||||||
|
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
private readonly ISyncService _syncService;
|
||||||
|
private readonly ICipherService _cipherService;
|
||||||
private AuthenticatorPageViewModel _vm;
|
private AuthenticatorPageViewModel _vm;
|
||||||
private readonly bool _fromTabPage;
|
private readonly bool _fromTabPage;
|
||||||
private readonly Action<string> _selectAction;
|
private readonly Action<string> _selectAction;
|
||||||
@@ -26,6 +29,8 @@ namespace Bit.App.Pages
|
|||||||
//_tabsPage = tabsPage;
|
//_tabsPage = tabsPage;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
//_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
//_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_vm = BindingContext as AuthenticatorPageViewModel;
|
_vm = BindingContext as AuthenticatorPageViewModel;
|
||||||
//_vm.Page = this;
|
//_vm.Page = this;
|
||||||
//_fromTabPage = fromTabPage;
|
//_fromTabPage = fromTabPage;
|
||||||
@@ -47,7 +52,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await _vm.InitAsync();
|
await _vm.LoadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
@@ -67,7 +72,36 @@ namespace Bit.App.Pages
|
|||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
//});
|
//});
|
||||||
|
|
||||||
|
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||||
|
{
|
||||||
|
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _vm.LoadAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e) when (e.Message.Contains("No key."))
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
await _vm.LoadAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Task.Delay(5000);
|
||||||
|
if (!_vm.Loaded)
|
||||||
|
{
|
||||||
|
await _vm.LoadAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustToolbar();
|
||||||
|
//await CheckAddRequest();
|
||||||
|
}, _mainContent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async void Search_Clicked(object sender, EventArgs e)
|
private async void Search_Clicked(object sender, EventArgs e)
|
||||||
@@ -132,6 +166,11 @@ namespace Bit.App.Pages
|
|||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
//_broadcasterService.Unsubscribe(nameof(GeneratorPage));
|
//_broadcasterService.Unsubscribe(nameof(GeneratorPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AdjustToolbar()
|
||||||
|
{
|
||||||
|
//_addItem.IsEnabled = !_vm.Deleted;
|
||||||
|
//_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
129
src/App/Pages/Authenticator/AuthenticatorPageListItem.cs
Normal file
129
src/App/Pages/Authenticator/AuthenticatorPageListItem.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class AuthenticatorPageListItem : ExtendedViewModel
|
||||||
|
{
|
||||||
|
//private string _totpCode;
|
||||||
|
private readonly ITotpService _totpService;
|
||||||
|
|
||||||
|
//public CipherView Cipher { get; set; }
|
||||||
|
//public CipherType? Type { get; set; }
|
||||||
|
//public int interval { get; set; }
|
||||||
|
//public long TotpSec { get; set; }
|
||||||
|
//private DateTime? _totpInterval = null;
|
||||||
|
|
||||||
|
private CipherView _cipher;
|
||||||
|
|
||||||
|
private bool _websiteIconsEnabled;
|
||||||
|
private string _iconImageSource = string.Empty;
|
||||||
|
|
||||||
|
public int interval { get; set; }
|
||||||
|
private string _totpSec;
|
||||||
|
|
||||||
|
private string _totpCode;
|
||||||
|
private string _totpCodeFormatted = "938 928";
|
||||||
|
|
||||||
|
|
||||||
|
public AuthenticatorPageListItem(CipherView cipherView, bool websiteIconsEnabled)
|
||||||
|
{
|
||||||
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
|
|
||||||
|
Cipher = cipherView;
|
||||||
|
WebsiteIconsEnabled = websiteIconsEnabled;
|
||||||
|
interval = _totpService.GetTimeInterval(Cipher.Login.Totp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Command CopyCommand { get; set; }
|
||||||
|
|
||||||
|
public CipherView Cipher
|
||||||
|
{
|
||||||
|
get => _cipher;
|
||||||
|
set => SetProperty(ref _cipher, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TotpCodeFormatted
|
||||||
|
{
|
||||||
|
get => _totpCodeFormatted;
|
||||||
|
set => SetProperty(ref _totpCodeFormatted, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TotpSec
|
||||||
|
{
|
||||||
|
get => _totpSec;
|
||||||
|
set => SetProperty(ref _totpSec, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool WebsiteIconsEnabled
|
||||||
|
{
|
||||||
|
get => _websiteIconsEnabled;
|
||||||
|
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowIconImage
|
||||||
|
{
|
||||||
|
get => WebsiteIconsEnabled
|
||||||
|
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
|
||||||
|
&& IconImageSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string IconImageSource
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_iconImageSource == string.Empty) // default value since icon source can return null
|
||||||
|
{
|
||||||
|
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
|
||||||
|
}
|
||||||
|
return _iconImageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TotpTickAsync()
|
||||||
|
{
|
||||||
|
var epoc = CoreHelpers.EpocUtcNow() / 1000;
|
||||||
|
var mod = epoc % interval;
|
||||||
|
var totpSec = interval - mod;
|
||||||
|
TotpSec = totpSec.ToString();
|
||||||
|
//TotpLow = totpSec < 7;
|
||||||
|
if (mod == 0)
|
||||||
|
{
|
||||||
|
await TotpUpdateCodeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TotpUpdateCodeAsync()
|
||||||
|
{
|
||||||
|
_totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
|
||||||
|
if (_totpCode != null)
|
||||||
|
{
|
||||||
|
if (_totpCode.Length > 4)
|
||||||
|
{
|
||||||
|
var half = (int)Math.Floor(_totpCode.Length / 2M);
|
||||||
|
TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
|
||||||
|
_totpCode.Substring(half));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TotpCodeFormatted = _totpCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TotpCodeFormatted = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -12,29 +14,35 @@ namespace Bit.App.Pages
|
|||||||
#region Members
|
#region Members
|
||||||
|
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IClipboardService _clipboardService;
|
||||||
private bool _showList = true;
|
private readonly ITotpService _totpService;
|
||||||
private bool _refreshing;
|
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
private readonly ICipherService _cipherService;
|
||||||
|
|
||||||
|
private bool _showList = true;
|
||||||
|
private bool _refreshing;
|
||||||
|
private bool _loaded;
|
||||||
|
private bool _websiteIconsEnabled = true;
|
||||||
|
//private long _totpSec;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Ctor
|
#region Ctor
|
||||||
|
|
||||||
public AuthenticatorPageViewModel()
|
public AuthenticatorPageViewModel()
|
||||||
{
|
{
|
||||||
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
|
|
||||||
PageTitle = AppResources.Authenticator;
|
PageTitle = AppResources.Authenticator;
|
||||||
|
Items = new ExtendedObservableCollection<AuthenticatorPageListItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public async Task InitAsync() { }
|
|
||||||
|
|
||||||
public async Task CopyAsync()
|
public async Task CopyAsync()
|
||||||
{
|
{
|
||||||
//await _clipboardService.CopyTextAsync(Password);
|
//await _clipboardService.CopyTextAsync(Password);
|
||||||
@@ -54,15 +62,61 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ShowList = true;
|
try
|
||||||
this.Refreshing = false;
|
{
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ShowList = true;
|
||||||
|
Refreshing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadDataAsync()
|
||||||
|
{
|
||||||
|
var _allCiphers = await _cipherService.GetAllDecryptedAsync();
|
||||||
|
_allCiphers = _allCiphers.Where(c => c.Type == Core.Enums.CipherType.Login && c.Login.Totp != null).ToList();
|
||||||
|
var filteredCiphers = _allCiphers.Select(c => new AuthenticatorPageListItem(c, WebsiteIconsEnabled)).ToList();
|
||||||
|
Items.ResetWithRange(filteredCiphers);
|
||||||
|
|
||||||
|
foreach (AuthenticatorPageListItem item in Items)
|
||||||
|
{
|
||||||
|
item.TotpUpdateCodeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
//await TotpUpdateCodeAsync();
|
||||||
|
// var interval = _totpService.GetTimeInterval(Cipher.Login.Totp);
|
||||||
|
// await TotpTickAsync(interval);
|
||||||
|
// _totpInterval = DateTime.UtcNow;
|
||||||
|
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
|
||||||
|
{
|
||||||
|
foreach(AuthenticatorPageListItem item in Items)
|
||||||
|
{
|
||||||
|
item.TotpTickAsync();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private async Task TotpTickAsync(int intervalSeconds)
|
||||||
|
//{
|
||||||
|
// var epoc = CoreHelpers.EpocUtcNow() / 1000;
|
||||||
|
// var mod = epoc % intervalSeconds;
|
||||||
|
// var totpSec = intervalSeconds - mod;
|
||||||
|
// TotpSec = totpSec.ToString();
|
||||||
|
// TotpLow = totpSec < 7;
|
||||||
|
// if (mod == 0)
|
||||||
|
// {
|
||||||
|
// await TotpUpdateCodeAsync();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
public ExtendedObservableCollection<GroupingsPageListGroup> Items { get; set; }
|
public ExtendedObservableCollection<AuthenticatorPageListItem> Items { get; set; }
|
||||||
public Command RefreshCommand { get; set; }
|
public Command RefreshCommand { get; set; }
|
||||||
|
|
||||||
public bool ShowList
|
public bool ShowList
|
||||||
@@ -77,6 +131,23 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _refreshing, value);
|
set => SetProperty(ref _refreshing, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool WebsiteIconsEnabled
|
||||||
|
{
|
||||||
|
get => _websiteIconsEnabled;
|
||||||
|
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Loaded
|
||||||
|
{
|
||||||
|
get => _loaded;
|
||||||
|
set => SetProperty(ref _loaded, value);
|
||||||
|
}
|
||||||
|
//public long TotpSec
|
||||||
|
//{
|
||||||
|
// get => _totpSec;
|
||||||
|
// set => SetProperty(ref _totpSec, value);
|
||||||
|
//}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user