diff --git a/src/Android/Properties/AndroidManifest.xml b/src/Android/Properties/AndroidManifest.xml index 3bac51932..6dd07923a 100644 --- a/src/Android/Properties/AndroidManifest.xml +++ b/src/Android/Properties/AndroidManifest.xml @@ -9,6 +9,7 @@ + diff --git a/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml new file mode 100644 index 000000000..00ecc3415 --- /dev/null +++ b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml.cs b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml.cs new file mode 100644 index 000000000..e4a53c988 --- /dev/null +++ b/src/App/Controls/AuthenticatorViewCell/AuthenticatorViewCell.xaml.cs @@ -0,0 +1,67 @@ +using System; +using Bit.App.Pages; +using Bit.App.Utilities; +using Bit.Core.Models.View; +using Bit.Core.Utilities; +using Xamarin.Forms; + +namespace Bit.App.Controls +{ + public partial class AuthenticatorViewCell : ExtendedGrid + { + public static readonly BindableProperty CipherProperty = BindableProperty.Create( + nameof(Cipher), typeof(CipherView), typeof(AuthenticatorViewCell), default(CipherView), BindingMode.TwoWay); + + public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create( + nameof(WebsiteIconsEnabled), typeof(bool?), typeof(AuthenticatorViewCell)); + + public static readonly BindableProperty TotpSecProperty = BindableProperty.Create( + nameof(TotpSec), typeof(long), typeof(AuthenticatorViewCell)); + + public AuthenticatorViewCell() + { + InitializeComponent(); + } + + public Command CopyCommand { get; set; } + + public CipherView Cipher + { + get => GetValue(CipherProperty) as CipherView; + set => SetValue(CipherProperty, value); + } + + public bool? WebsiteIconsEnabled + { + get => (bool)GetValue(WebsiteIconsEnabledProperty); + set => SetValue(WebsiteIconsEnabledProperty, value); + } + + public long TotpSec + { + get => (long)GetValue(TotpSecProperty); + set => SetValue(TotpSecProperty, value); + } + + public bool ShowIconImage + { + get => WebsiteIconsEnabled ?? false + && !string.IsNullOrWhiteSpace(Cipher.Login?.Uri) + && IconImageSource != null; + } + + private string _iconImageSource = string.Empty; + public string IconImageSource + { + get + { + if (_iconImageSource == string.Empty) // default value since icon source can return null + { + _iconImageSource = IconImageHelper.GetLoginIconImage(Cipher); + } + return _iconImageSource; + } + + } + } +} diff --git a/src/App/Controls/CircularProgressbarView.cs b/src/App/Controls/CircularProgressbarView.cs new file mode 100644 index 000000000..56e8a6c43 --- /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(); + 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/Pages/Vault/CipherAddEditPage.xaml b/src/App/Pages/Vault/CipherAddEditPage.xaml index 37855416f..2c8a03b59 100644 --- a/src/App/Pages/Vault/CipherAddEditPage.xaml +++ b/src/App/Pages/Vault/CipherAddEditPage.xaml @@ -184,31 +184,61 @@ +