diff --git a/src/App/Pages/Vault/ScanPage.xaml b/src/App/Pages/Vault/ScanPage.xaml index b7dd30ef0..2169c4cd9 100644 --- a/src/App/Pages/Vault/ScanPage.xaml +++ b/src/App/Pages/Vault/ScanPage.xaml @@ -6,6 +6,8 @@ xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:controls="clr-namespace:Bit.App.Controls" + xmlns:forms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms" + xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore" xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms" x:Name="_page" Title="{u:I18n ScanQrTitle}"> @@ -27,14 +29,12 @@ - - + OnScanResult="OnScanResult"/> + + + + + + _logger = new LazyResolve("logger"); @@ -34,6 +41,10 @@ namespace Bit.App.Pages { ToolbarItems.RemoveAt(0); } + + _greenColor = ThemeManager.GetResourceColor("SuccessColor"); + _greenSKColor = _greenColor.ToSKColor(); + _blueSKColor = ThemeManager.GetResourceColor("PrimaryColor").ToSKColor(); } protected override void OnAppearing() @@ -69,6 +80,8 @@ namespace Bit.App.Pages _logger.Value.Exception(ex); } }, autofocusCts.Token); + _pageIsActive = true; + AnimationLoop(); } protected override async void OnDisappearing() @@ -78,6 +91,7 @@ namespace Bit.App.Pages await _continuousAutofocusTask; _zxing.IsScanning = false; + _pageIsActive = false; base.OnDisappearing(); } @@ -91,8 +105,12 @@ namespace Bit.App.Pages { if (text.StartsWith("otpauth://totp")) { - Vibration.Vibrate(); - _callback(text); + Task.Run(async () => + { + _qrcodeFound = true; + await Task.Delay(2000); + _callback(text); + }); return; } else if (Uri.TryCreate(text, UriKind.Absolute, out Uri uri) && @@ -103,8 +121,12 @@ namespace Bit.App.Pages { if (part.StartsWith("secret=")) { - Vibration.Vibrate(); - _callback(part.Substring(7)?.ToUpperInvariant()); + Task.Run(async () => + { + _qrcodeFound = true; + await Task.Delay(2000); + _callback(part.Substring(7)?.ToUpperInvariant()); + }); return; } } @@ -125,5 +147,65 @@ namespace Bit.App.Pages { ViewModel.ToggleScanModeCommand.Execute(null); } + + private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) + { + SKImageInfo info = args.Info; + SKSurface surface = args.Surface; + SKCanvas canvas = surface.Canvas; + var margins = 20; + var maxSquareSize = (Math.Min(info.Height, info.Width)*0.9f - margins) * _scale; + var squareSize = maxSquareSize; + var lineSize = squareSize * 0.15f; + var startXPoint = (info.Width / 2) - (squareSize / 2); + var startYPoint = (info.Height / 2) - (squareSize / 2); + canvas.Clear(SKColors.Transparent); + + using (SKPaint strokePaint = new SKPaint + { + Color = _qrcodeFound ? _greenSKColor : _blueSKColor, + StrokeWidth = 9*_scale, + StrokeCap = SKStrokeCap.Round, + }) + { + canvas.Scale(1,1); + //top left + canvas.DrawLine (startXPoint, startYPoint, startXPoint, startYPoint+lineSize, strokePaint); + canvas.DrawLine (startXPoint, startYPoint, startXPoint+lineSize, startYPoint, strokePaint); + //bot left + canvas.DrawLine (startXPoint, startYPoint+squareSize, startXPoint, startYPoint+squareSize-lineSize, strokePaint); + canvas.DrawLine (startXPoint, startYPoint+squareSize, startXPoint+lineSize, startYPoint+squareSize, strokePaint); + //top right + canvas.DrawLine (startXPoint+squareSize, startYPoint, startXPoint+squareSize-lineSize, startYPoint, strokePaint); + canvas.DrawLine (startXPoint+squareSize, startYPoint, startXPoint+squareSize, startYPoint+lineSize, strokePaint); + //bot right + canvas.DrawLine (startXPoint+squareSize, startYPoint+squareSize, startXPoint+squareSize-lineSize, startYPoint+squareSize, strokePaint); + canvas.DrawLine (startXPoint+squareSize, startYPoint+squareSize, startXPoint+squareSize, startYPoint+squareSize-lineSize, strokePaint); + } + } + + Stopwatch _stopwatch = new Stopwatch(); + bool _pageIsActive; + bool _qrcodeFound = false; + float _scale; + async Task AnimationLoop() + { + _stopwatch.Start(); + while (_pageIsActive) + { + 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) + { + Vibration.Vibrate(); + _checkIcon.TextColor = _greenColor; + SkCanvasView.InvalidateSurface(); + break; + } + } + _stopwatch.Stop(); + } } }