diff --git a/src/App/App.cs b/src/App/App.cs index bb5850ef7..8a3d9883c 100644 --- a/src/App/App.cs +++ b/src/App/App.cs @@ -117,7 +117,10 @@ namespace Bit.App } else if(pinUnlock) { - + if(Current.MainPage.Navigation.ModalStack.LastOrDefault() as LockPinPage == null) + { + await Current.MainPage.Navigation.PushModalAsync(new LockPinPage(), false); + } } else { diff --git a/src/App/App.csproj b/src/App/App.csproj index 5d1dcde99..b242631d4 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -80,8 +80,10 @@ + + diff --git a/src/App/Models/Page/PinPageModel.cs b/src/App/Models/Page/PinPageModel.cs new file mode 100644 index 000000000..d3819a877 --- /dev/null +++ b/src/App/Models/Page/PinPageModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Bit.App.Models.Page +{ + public class PinPageModel : INotifyPropertyChanged + { + private string _labelText; + private List _pin; + + public PinPageModel() + { + LabelText = "_ _ _ _"; + PIN = new List(); + } + + public event PropertyChangedEventHandler PropertyChanged; + + public string LabelText + { + get { return _labelText; } + set + { + _labelText = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LabelText))); + } + } + public List PIN + { + get { return _pin; } + set + { + _pin = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(PIN))); + } + } + } +} diff --git a/src/App/Pages/LockPinPage.cs b/src/App/Pages/LockPinPage.cs new file mode 100644 index 000000000..2e22b672f --- /dev/null +++ b/src/App/Pages/LockPinPage.cs @@ -0,0 +1,159 @@ +using System; +using System.Threading.Tasks; +using Acr.UserDialogs; +using Bit.App.Abstractions; +using Bit.App.Resources; +using Xamarin.Forms; +using XLabs.Ioc; +using Plugin.Settings.Abstractions; +using System.Collections.Generic; +using Bit.App.Models.Page; + +namespace Bit.App.Pages +{ + public class LockPinPage : ContentPage + { + private readonly IAuthService _authService; + private readonly IUserDialogs _userDialogs; + private readonly ISettings _settings; + + public LockPinPage() + { + _authService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); + _settings = Resolver.Resolve(); + + Init(); + } + + public PinPageModel Model { get; set; } = new PinPageModel(); + + public void Init() + { + var label = new Label + { + HorizontalTextAlignment = TextAlignment.Center, + FontSize = 40, + TextColor = Color.FromHex("333333") + }; + label.SetBinding(Label.TextProperty, s => s.LabelText); + + var grid = new Grid(); + grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + + Action updateLabelAction = new Action(UpdateLabel); + grid.Children.Add(new PinButton("1", this, updateLabelAction), 0, 0); + grid.Children.Add(new PinButton("2", this, updateLabelAction), 1, 0); + grid.Children.Add(new PinButton("3", this, updateLabelAction), 2, 0); + + grid.Children.Add(new PinButton("4", this, updateLabelAction), 0, 1); + grid.Children.Add(new PinButton("5", this, updateLabelAction), 1, 1); + grid.Children.Add(new PinButton("6", this, updateLabelAction), 2, 1); + + grid.Children.Add(new PinButton("7", this, updateLabelAction), 0, 2); + grid.Children.Add(new PinButton("8", this, updateLabelAction), 1, 2); + grid.Children.Add(new PinButton("9", this, updateLabelAction), 2, 2); + + grid.Children.Add(new Label(), 0, 3); + grid.Children.Add(new PinButton("0", this, updateLabelAction), 1, 3); + grid.Children.Add(new DeleteButton(this, updateLabelAction), 2, 3); + + var logoutButton = new Button + { + Text = AppResources.LogOut, + Command = new Command(async () => await LogoutAsync()), + VerticalOptions = LayoutOptions.End + }; + + var stackLayout = new StackLayout + { + Padding = new Thickness(30, 40), + Spacing = 10, + Children = { label, grid, logoutButton } + }; + + Title = "Verify PIN"; + Content = stackLayout; + BindingContext = Model; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + } + + private async Task LogoutAsync() + { + if(!await _userDialogs.ConfirmAsync("Are you sure you want to log out?", null, AppResources.Yes, AppResources.Cancel)) + { + return; + } + + _authService.LogOut(); + await Navigation.PopModalAsync(); + Application.Current.MainPage = new LoginNavigationPage(); + } + + private void UpdateLabel() + { + var newText = string.Empty; + for(int i = 0; i < 4; i++) + { + if(Model.PIN.Count <= i) + { + newText += "_ "; + } + else + { + newText += "* "; + } + } + + Model.LabelText = newText.TrimEnd(); + } + + public class PinButton : Button + { + public PinButton(string text, LockPinPage page, Action updateLabelAction) + { + Text = text; + Command = new Command(() => + { + if(page.Model.PIN.Count >= 4) + { + return; + } + + page.Model.PIN.Add(text); + updateLabelAction(); + }); + CommandParameter = text; + } + } + + public class DeleteButton : Button + { + public DeleteButton(LockPinPage page, Action updateLabelAction) + { + Text = "Delete"; + Command = new Command(() => + { + if(page.Model.PIN.Count == 0) + { + return; + } + + page.Model.PIN.RemoveAt(page.Model.PIN.Count - 1); + updateLabelAction(); + }); + } + } + } +}