diff --git a/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml
index a6e9ac662..cc28c2251 100644
--- a/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml
+++ b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml
@@ -9,30 +9,23 @@
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
StyleClass="list-row, list-row-platform"
HorizontalOptions="FillAndExpand"
- x:DataType="pages:GroupingsPageTOTPListItem">
+ x:DataType="pages:GroupingsPageTOTPListItem"
+ ColumnDefinitions="40,*,40,Auto,40"
+ RowSpacing="0"
+ Padding="0,10,0,0"
+ RowDefinitions="Auto,Auto">
-
-
-
-
-
-
-
-
-
-
-
-
@@ -46,47 +39,35 @@
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
+ Grid.RowSpan="2"
IsVisible="{Binding ShowIconImage}"
Source="{Binding IconImageSource, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="False" />
-
-
-
-
-
+ Grid.Row="0"
+ VerticalTextAlignment="End"
+ VerticalOptions="End"
+ StyleClass="list-title, list-title-platform"
+ Text="{Binding Cipher.Name}" />
-
-
-
+
+ HorizontalTextAlignment="Center"
+ HorizontalOptions="Fill"
+ VerticalTextAlignment="Center"
+ VerticalOptions="Fill" />
+ HorizontalOptions="Fill"
+ VerticalOptions="Fill">
_totpCodeFormatted;
- set => _totpCodeFormatted = value;
- }
}
}
diff --git a/src/App/Controls/CircularProgressbarView.cs b/src/App/Controls/CircularProgressbarView.cs
new file mode 100644
index 000000000..a4eec0341
--- /dev/null
+++ b/src/App/Controls/CircularProgressbarView.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Runtime.CompilerServices;
+using SkiaSharp;
+using SkiaSharp.Views.Forms;
+using Xamarin.Essentials;
+using Xamarin.Forms;
+
+namespace Bit.App.Controls
+{
+ public class CircularProgressbarView : SKCanvasView
+ {
+ private Circle _circle;
+
+ public static readonly BindableProperty ProgressProperty = BindableProperty.Create(
+ nameof(Progress), typeof(double), typeof(CircularProgressbarView), propertyChanged: OnProgressChanged);
+
+ public static readonly BindableProperty RadiusProperty = BindableProperty.Create(
+ nameof(Radius), typeof(float), typeof(CircularProgressbarView), 15f);
+
+ public static readonly BindableProperty StrokeWidthProperty = BindableProperty.Create(
+ nameof(StrokeWidth), typeof(float), typeof(CircularProgressbarView), 3f);
+
+ public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create(
+ nameof(ProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
+
+ public static readonly BindableProperty EndingProgressColorProperty = BindableProperty.Create(
+ nameof(EndingProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
+
+ public static readonly BindableProperty BackgroundProgressColorProperty = BindableProperty.Create(
+ nameof(BackgroundProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.Default);
+
+ public double Progress
+ {
+ get { return (double)GetValue(ProgressProperty); }
+ set { SetValue(ProgressProperty, value); }
+ }
+
+ public float Radius
+ {
+ get => (float)GetValue(RadiusProperty);
+ set => SetValue(RadiusProperty, value);
+ }
+ public float StrokeWidth
+ {
+ get => (float)GetValue(StrokeWidthProperty);
+ set => SetValue(StrokeWidthProperty, value);
+ }
+
+ public Color ProgressColor
+ {
+ get => (Color)GetValue(ProgressColorProperty);
+ set => SetValue(ProgressColorProperty, value);
+ }
+
+ public Color EndingProgressColor
+ {
+ get => (Color)GetValue(EndingProgressColorProperty);
+ set => SetValue(EndingProgressColorProperty, value);
+ }
+
+ public Color BackgroundProgressColor
+ {
+ get => (Color)GetValue(BackgroundProgressColorProperty);
+ set => SetValue(BackgroundProgressColorProperty, value);
+ }
+
+ private static void OnProgressChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ var context = bindable as CircularProgressbarView;
+ context.InvalidateSurface();
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+ if (propertyName == nameof(Progress))
+ {
+ _circle = new Circle(Radius * (float)DeviceDisplay.MainDisplayInfo.Density, (info) => new SKPoint((float)info.Width / 2, (float)info.Height / 2));
+ }
+ }
+
+ protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
+ {
+ base.OnPaintSurface(e);
+ if(_circle != null)
+ {
+ _circle.CalculateCenter(e.Info);
+ e.Surface.Canvas.Clear();
+ DrawCircle(e.Surface.Canvas, _circle, StrokeWidth * (float)DeviceDisplay.MainDisplayInfo.Density, BackgroundProgressColor.ToSKColor());
+ DrawArc(e.Surface.Canvas, _circle, () => (float)Progress, StrokeWidth * (float)DeviceDisplay.MainDisplayInfo.Density, ProgressColor.ToSKColor(), EndingProgressColor.ToSKColor());
+ }
+ }
+
+ private void DrawCircle(SKCanvas canvas, Circle circle, float strokewidth, SKColor color)
+ {
+ canvas.DrawCircle(circle.Center, circle.Redius,
+ new SKPaint()
+ {
+ StrokeWidth = strokewidth,
+ Color = color,
+ IsStroke = true,
+ IsAntialias = true
+ });
+ }
+
+ private void DrawArc(SKCanvas canvas, Circle circle, Func progress, float strokewidth, SKColor color, SKColor progressEndColor)
+ {
+ var progressValue = progress.Invoke();
+ var angle = progressValue * 3.6f;
+ canvas.DrawArc(circle.Rect, 270, angle, false,
+ new SKPaint()
+ {
+ StrokeWidth = strokewidth,
+ Color = progressValue < 20f ? progressEndColor : color,
+ IsStroke = true,
+ IsAntialias = true
+ });
+ }
+ }
+
+ public class Circle
+ {
+ private readonly Func _centerFunc;
+
+ public Circle(float redius, Func centerFunc)
+ {
+ _centerFunc = centerFunc;
+ Redius = redius;
+ }
+ public SKPoint Center { get; set; }
+ public float Redius { get; set; }
+ public SKRect Rect => new SKRect(Center.X - Redius, Center.Y - Redius, Center.X + Redius, Center.Y + Redius);
+
+ public void CalculateCenter(SKImageInfo argsInfo)
+ {
+ Center = _centerFunc(argsInfo);
+ }
+ }
+}
diff --git a/src/App/Controls/CircularProgressbarView.xaml b/src/App/Controls/CircularProgressbarView.xaml
deleted file mode 100644
index 02f000645..000000000
--- a/src/App/Controls/CircularProgressbarView.xaml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
diff --git a/src/App/Controls/CircularProgressbarView.xaml.cs b/src/App/Controls/CircularProgressbarView.xaml.cs
deleted file mode 100644
index eebb146ac..000000000
--- a/src/App/Controls/CircularProgressbarView.xaml.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using SkiaSharp;
-using SkiaSharp.Views.Forms;
-using Xamarin.Essentials;
-using Xamarin.Forms;
-
-namespace Bit.App.Controls
-{
- public partial class CircularProgressbarView : ContentView
- {
- private ProgressDrawer _progressDrawer;
-
- public static readonly BindableProperty ProgressProperty = BindableProperty.Create(
- "Progress", typeof(double), typeof(CircularProgressbarView), propertyChanged: OnProgressChanged);
-
- public double Progress
- {
- get { return (double)GetValue(ProgressProperty); }
- set { SetValue(ProgressProperty, value); }
- }
-
- public float Radius { get; set; }
- public float StrokeWidth { get; set; }
- public Color ProgressColor { get; set; }
- public Color EndingProgressColor { get; set; }
- public Color BackgroundProgressColor { get; set; }
-
- private static void OnProgressChanged(BindableObject bindable, object oldvalue, object newvalue)
- {
- var context = bindable as CircularProgressbarView;
- context.SkCanvasView.InvalidateSurface();
- }
-
- public CircularProgressbarView()
- {
- InitializeComponent();
- }
-
- protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- base.OnPropertyChanged(propertyName);
- if (propertyName == nameof(Progress) && _progressDrawer == null)
- {
- var circle = new Circle(Radius * (float)DeviceDisplay.MainDisplayInfo.Density, (info) => new SKPoint((float)info.Width / 2, (float)info.Height / 2));
- _progressDrawer = new ProgressDrawer(SkCanvasView, circle, () => (float)Progress, StrokeWidth * (float)DeviceDisplay.MainDisplayInfo.Density, BackgroundProgressColor.ToSKColor(), ProgressColor.ToSKColor(), EndingProgressColor.ToSKColor());
- }
- }
- }
-
- public class Circle
- {
- private readonly Func _centerfunc;
-
- public Circle(float redius, Func centerfunc)
- {
- _centerfunc = centerfunc;
- Redius = redius;
- }
- public SKPoint Center { get; set; }
- public float Redius { get; set; }
- public SKRect Rect => new SKRect(Center.X - Redius, Center.Y - Redius, Center.X + Redius, Center.Y + Redius);
-
- public void CalculateCenter(SKImageInfo argsInfo)
- {
- Center = _centerfunc.Invoke(argsInfo);
- }
- }
-
- public class ProgressDrawer
- {
- public ProgressDrawer(SKCanvasView canvas, Circle circle, Func progress, float strokeWidth, SKColor progressColor, SKColor foregroundColor, SKColor progressEndColor)
- {
- canvas.PaintSurface += (sender, args) =>
- {
- circle.CalculateCenter(args.Info);
- args.Surface.Canvas.Clear();
- DrawCircle(args.Surface.Canvas, circle, strokeWidth, progressColor);
- DrawArc(args.Surface.Canvas, circle, progress, strokeWidth, foregroundColor, progressEndColor);
- };
- }
-
- private void DrawCircle(SKCanvas canvas, Circle circle, float strokewidth, SKColor color)
- {
- canvas.DrawCircle(circle.Center, circle.Redius,
- new SKPaint()
- {
- StrokeWidth = strokewidth,
- Color = color,
- IsStroke = true,
- IsAntialias = true
- });
- }
-
- private void DrawArc(SKCanvas canvas, Circle circle, Func progress, float strokewidth, SKColor color, SKColor progressEndColor)
- {
- var progressValue = progress.Invoke();
- var angle = progressValue * 3.6f;
- canvas.DrawArc(circle.Rect, 270, angle, false,
- new SKPaint()
- {
- StrokeWidth = strokewidth,
- Color = progressValue < 20f ? progressEndColor : color,
- IsStroke = true,
- IsAntialias = true
- });
- }
- }
-}
diff --git a/src/App/Pages/Vault/AddEditPageViewModel.cs b/src/App/Pages/Vault/AddEditPageViewModel.cs
index 6a56be80e..1d121b602 100644
--- a/src/App/Pages/Vault/AddEditPageViewModel.cs
+++ b/src/App/Pages/Vault/AddEditPageViewModel.cs
@@ -11,6 +11,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
+using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -92,7 +93,7 @@ namespace Bit.App.Pages
UriOptionsCommand = new Command(UriOptions);
FieldOptionsCommand = new Command(FieldOptions);
PasswordPromptHelpCommand = new Command(PasswordPromptHelp);
- CopyCommand = new Command(CopyTotpClipboard);
+ CopyCommand = new AsyncCommand(CopyTotpClipboardAsync);
Uris = new ExtendedObservableCollection();
Fields = new ExtendedObservableCollection();
Collections = new ExtendedObservableCollection();
@@ -154,7 +155,7 @@ namespace Bit.App.Pages
public Command UriOptionsCommand { get; set; }
public Command FieldOptionsCommand { get; set; }
public Command PasswordPromptHelpCommand { get; set; }
- public Command CopyCommand { get; set; }
+ public AsyncCommand CopyCommand { get; set; }
public string CipherId { get; set; }
public string OrganizationId { get; set; }
public string FolderId { get; set; }
@@ -305,8 +306,8 @@ namespace Bit.App.Pages
public bool AllowPersonal { get; set; }
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
- public bool HasTotpValue => !string.IsNullOrEmpty(Cipher.Login.Totp);
- public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTOTP}";
+ public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
+ public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
public void Init()
{
PageTitle = EditMode && !CloneMode ? AppResources.EditItem : AppResources.AddItem;
@@ -865,10 +866,17 @@ namespace Bit.App.Pages
}
}
- private async void CopyTotpClipboard()
+ private async Task CopyTotpClipboardAsync()
{
- await _clipboardService.CopyTextAsync(_cipher.Login.Totp);
- _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.AuthenticatorKeyScanner));
+ try
+ {
+ await _clipboardService.CopyTextAsync(_cipher.Login.Totp);
+ _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.AuthenticatorKeyScanner));
+ }
+ catch (Exception ex)
+ {
+ _logger.Exception(ex);
+ }
}
}
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml b/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
index 474717edd..7ef525d43 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
@@ -6,6 +6,7 @@
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:controls="clr-namespace:Bit.App.Controls"
+ xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:GroupingsPageViewModel"
Title="{Binding PageTitle}"
@@ -143,7 +144,6 @@
+ AutomationProperties.IsInAccessibleTree="True"
+ AutomationProperties.Name="{Binding ShowTotpCodesAccessibilityText}">
+
+
+
+
_logger = new LazyResolve("logger");
private readonly ITotpService _totpService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IClipboardService _clipboardService;
@@ -23,8 +24,8 @@ namespace Bit.App.Pages
public int interval { get; set; }
private double _progress;
private string _totpSec;
- private string _totpCode;
- private string _totpCodeFormatted = "938 928";
+ private string _totpCodeFormatted;
+ private TotpHelper _totpTickHelper;
public GroupingsPageTOTPListItem(CipherView cipherView, bool websiteIconsEnabled)
@@ -39,10 +40,9 @@ namespace Bit.App.Pages
CopyCommand = new AsyncCommand(CopyToClipboardAsync,
onException: ex => _logger.Value.Exception(ex),
allowsMultipleExecutions: false);
+ _totpTickHelper = new TotpHelper(cipherView);
}
- readonly LazyResolve _logger = new LazyResolve("logger");
-
public AsyncCommand CopyCommand { get; set; }
public CipherView Cipher
@@ -110,39 +110,10 @@ namespace Bit.App.Pages
public async Task TotpTickAsync()
{
- var epoc = CoreHelpers.EpocUtcNow() / 1000;
- var mod = epoc % interval;
- var totpSec = interval - mod;
- TotpSec = totpSec.ToString();
- Progress = totpSec * 100 / 30;
- //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;
- }
+ await _totpTickHelper.GenerateNewTotpValues();
+ TotpSec = _totpTickHelper.TotpSec;
+ Progress = _totpTickHelper.Progress;
+ TotpCodeFormatted = _totpTickHelper.TotpCodeFormatted;
}
}
}
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
index cce301624..72bb1b688 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
@@ -38,7 +39,8 @@ namespace Bit.App.Pages
private Dictionary _collectionCounts = new Dictionary();
private Dictionary _typeCounts = new Dictionary();
private int _deletedCount = 0;
-
+ private CancellationTokenSource _totpTickCancellationToken;
+ private Task _totpTickTask;
private readonly ICipherService _cipherService;
private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService;
@@ -99,6 +101,9 @@ namespace Bit.App.Pages
public bool HasCiphers { get; set; }
public bool HasFolders { get; set; }
public bool HasCollections { get; set; }
+ public string ShowTotpCodesAccessibilityText => TotpFilterEnable ?
+ AppResources.AuthenticationCodesListIsVisibleActivateToShowCipherList
+ : AppResources.CipherListIsVisibleActivateToShowAuthenticationCodesList;
public bool ShowNoFolderCipherGroup => NoFolderCiphers != null
&& NoFolderCiphers.Count < NoFolderListSize
&& (Collections is null || !Collections.Any());
@@ -292,33 +297,7 @@ namespace Bit.App.Pages
}
if (Ciphers?.Any() ?? false)
{
- if (TotpFilterEnable)
- {
- var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted && !string.IsNullOrEmpty(c.Login.Totp))
- .Select(c => new GroupingsPageTOTPListItem(c, true)).ToList();
- groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
- ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
-
- foreach (var item in ciphersListItems)
- {
- await item.TotpUpdateCodeAsync();
- }
- Device.StartTimer(new TimeSpan(0, 0, 1), () =>
- {
- foreach (var item in ciphersListItems)
- {
- item.TotpTickAsync();
- }
- return TotpFilterEnable;
- });
- }
- else
- {
- var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted)
- .Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
- groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
- ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
- }
+ CreateCipherGroupedItems(ref groupedItems);
}
if (ShowNoFolderCipherGroup)
{
@@ -406,6 +385,45 @@ namespace Bit.App.Pages
}
}
+ private void CreateCipherGroupedItems(ref List groupedItems)
+ {
+ var uppercaseGroupNames = _deviceActionService.DeviceType == DeviceType.iOS;
+ _totpTickCancellationToken?.Cancel();
+ if (TotpFilterEnable)
+ {
+ var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted && !string.IsNullOrEmpty(c.Login.Totp))
+ .Select(c => new GroupingsPageTOTPListItem(c, true)).ToList();
+ groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
+ ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
+
+ StartCiphersTotpTick(ciphersListItems);
+ }
+ else
+ {
+ var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted)
+ .Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
+ groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
+ ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
+ }
+ }
+
+ private void StartCiphersTotpTick(List ciphersListItems)
+ {
+ _totpTickCancellationToken?.Cancel();
+ _totpTickCancellationToken = new CancellationTokenSource();
+ _totpTickTask = new TimerTask(() => { ciphersListItems.ForEach(i => i.TotpTickAsync()); }, _totpTickCancellationToken).Run();
+ }
+
+ public async Task StopCiphersTotpTick()
+ {
+ TotpFilterEnable = false;
+ _totpTickCancellationToken?.Cancel();
+ if (_totpTickTask != null)
+ {
+ await _totpTickTask;
+ }
+ }
+
public void DisableRefreshing()
{
Refreshing = false;
diff --git a/src/App/Pages/Vault/ScanPage.xaml b/src/App/Pages/Vault/ScanPage.xaml
index faccdd58f..9c1b31346 100644
--- a/src/App/Pages/Vault/ScanPage.xaml
+++ b/src/App/Pages/Vault/ScanPage.xaml
@@ -70,7 +70,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.CheckCircle}}"
HorizontalOptions="Center"
VerticalOptions="Start"
- FontSize="30"
+ FontSize="Title"
TextColor="Transparent"/>
+ Text="{u:I18n EnterKeyManually}"
+ FontSize="Title" />
@@ -121,12 +121,7 @@
AutomationId="zxingDefaultOverlay_TopTextLabel"
Margin="30,15,30,0"
HorizontalOptions="Center"
- StyleClass="text-sm"
- TextColor="White" />
-
diff --git a/src/App/Pages/Vault/ScanPage.xaml.cs b/src/App/Pages/Vault/ScanPage.xaml.cs
index b1e55ba8e..0ccf938cf 100644
--- a/src/App/Pages/Vault/ScanPage.xaml.cs
+++ b/src/App/Pages/Vault/ScanPage.xaml.cs
@@ -112,33 +112,44 @@ namespace Bit.App.Pages
private async void OnScanResult(ZXing.Result result)
{
- // Stop analysis until we navigate away so we don't keep reading barcodes
- _zxing.IsAnalyzing = false;
- var text = result?.Text;
- if (!string.IsNullOrWhiteSpace(text))
+ try
{
- if (text.StartsWith("otpauth://totp"))
+ // Stop analysis until we navigate away so we don't keep reading barcodes
+ _zxing.IsAnalyzing = false;
+ var text = result?.Text;
+ if (!string.IsNullOrWhiteSpace(text))
{
- await QrCodeFoundAsync();
- _callback(text);
- return;
- }
- else if (Uri.TryCreate(text, UriKind.Absolute, out Uri uri) &&
- !string.IsNullOrWhiteSpace(uri?.Query))
- {
- var queryParts = uri.Query.Substring(1).ToLowerInvariant().Split('&');
- foreach (var part in queryParts)
+ if (text.StartsWith("otpauth://totp"))
{
- if (part.StartsWith("secret="))
+ await QrCodeFoundAsync();
+ _callback(text);
+ return;
+ }
+ else if (Uri.TryCreate(text, UriKind.Absolute, out Uri uri) &&
+ !string.IsNullOrWhiteSpace(uri?.Query))
+ {
+ var queryParts = uri.Query.Substring(1).ToLowerInvariant().Split('&');
+ foreach (var part in queryParts)
{
- await QrCodeFoundAsync();
- _callback(part.Substring(7)?.ToUpperInvariant());
- return;
+ if (part.StartsWith("secret="))
+ {
+ await QrCodeFoundAsync();
+ var subResult = part.Substring(7);
+ if (!string.IsNullOrEmpty(subResult))
+ {
+ _callback(subResult.ToUpperInvariant());
+ }
+ return;
+ }
}
}
}
+ _callback(null);
+ }
+ catch (Exception ex)
+ {
+ _logger?.Value?.Exception(ex);
}
- _callback(null);
}
private async Task QrCodeFoundAsync()
@@ -228,23 +239,35 @@ namespace Bit.App.Pages
canvas.DrawLine(startXPoint + squareSize, startYPoint + squareSize, startXPoint + squareSize, startYPoint + squareSize - lineSize, strokePaint);
}
}
+
async Task AnimationLoopAsync()
{
- _stopwatch.Start();
- while (_pageIsActive)
+ try
{
- var t = _stopwatch.Elapsed.TotalSeconds % 2 / 2;
- _scale = (20 - (1 - (float)Math.Sin(4 * Math.PI * t))) / 20;
- SkCanvasView.InvalidateSurface();
- await Task.Delay(TimeSpan.FromSeconds(1.0 / 30));
- if (_qrcodeFound && _scale > 0.98f)
+ _stopwatch.Start();
+ while (_pageIsActive)
{
- _checkIcon.TextColor = _greenColor;
+ var t = _stopwatch.Elapsed.TotalSeconds % 2 / 2;
+ _scale = (20 - (1 - (float)Math.Sin(4 * Math.PI * t))) / 20;
SkCanvasView.InvalidateSurface();
- break;
+ await Task.Delay(TimeSpan.FromSeconds(1.0 / 30));
+ if (_qrcodeFound && _scale > 0.98f)
+ {
+ _checkIcon.TextColor = _greenColor;
+ SkCanvasView.InvalidateSurface();
+ break;
+ }
}
+ _stopwatch.Stop();
+ }
+ catch (Exception ex)
+ {
+ _logger?.Value?.Exception(ex);
+ }
+ finally
+ {
+ _stopwatch?.Stop();
}
- _stopwatch.Stop();
}
}
}
diff --git a/src/App/Pages/Vault/ScanPageViewModel.cs b/src/App/Pages/Vault/ScanPageViewModel.cs
index f7a2c184b..00405756b 100644
--- a/src/App/Pages/Vault/ScanPageViewModel.cs
+++ b/src/App/Pages/Vault/ScanPageViewModel.cs
@@ -7,19 +7,17 @@ namespace Bit.App.Pages
{
public class ScanPageViewModel : BaseViewModel
{
- private bool _showScanner;
+ private bool _showScanner = true;
private string _totpAuthenticationKey;
public ScanPageViewModel()
{
- ShowScanner = true;
- ToggleScanModeCommand = new Command(ToggleScanAsync);
+ ToggleScanModeCommand = new Command(() => ShowScanner = !ShowScanner);
}
public Command ToggleScanModeCommand { get; set; }
public string ScanQrPageTitle => ShowScanner ? AppResources.ScanQrTitle : AppResources.AuthenticatorKeyScanner;
- public string CameraInstructionTop => ShowScanner ? AppResources.CameraInstructionTop : AppResources.OnceTheKeyIsSuccessfullyEntered;
- public string CameraInstructionBottom => ShowScanner ? AppResources.CameraInstructionBottom : AppResources.SelectAddTotpToStoreTheKeySafely;
+ public string CameraInstructionTop => ShowScanner ? AppResources.PointYourCameraAtTheQRCode : AppResources.OnceTheKeyIsSuccessfullyEntered;
public string TotpAuthenticationKey
{
get => _totpAuthenticationKey;
@@ -37,8 +35,7 @@ namespace Bit.App.Pages
{
nameof(ToggleScanModeLabel),
nameof(ScanQrPageTitle),
- nameof(CameraInstructionTop),
- nameof(CameraInstructionBottom)
+ nameof(CameraInstructionTop)
});
}
@@ -54,16 +51,11 @@ namespace Bit.App.Pages
});
fs.Spans.Add(new Span
{
- Text = ShowScanner ? AppResources.EnterCodeManually : AppResources.ScanQRCode,
+ Text = ShowScanner ? AppResources.EnterKeyManually : AppResources.ScanQRCode,
TextColor = ThemeManager.GetResourceColor("ScanningToggleModeTextColor")
});
return fs;
}
}
-
- private void ToggleScanAsync()
- {
- ShowScanner = !ShowScanner;
- }
}
}
diff --git a/src/App/Pages/Vault/ViewPage.xaml b/src/App/Pages/Vault/ViewPage.xaml
index b046b6280..be877a223 100644
--- a/src/App/Pages/Vault/ViewPage.xaml
+++ b/src/App/Pages/Vault/ViewPage.xaml
@@ -187,11 +187,6 @@
VerticalOptions="Start" />